@glimmer/reference
Advanced tools
Comparing version
@@ -1,372 +0,2 @@ | ||
import { getProp, setProp, toIterator, getPath } from '@glimmer/global-context'; | ||
import { expect, isDict, EMPTY_ARRAY, isObject } from '@glimmer/util'; | ||
import { CONSTANT_TAG, validateTag, track, valueForTag, consumeTag, INITIAL, createTag, dirtyTag } from '@glimmer/validator'; | ||
const REFERENCE = Symbol('REFERENCE'); | ||
const CONSTANT = 0; | ||
const COMPUTE = 1; | ||
const UNBOUND = 2; | ||
const INVOKABLE = 3; | ||
////////// | ||
class ReferenceImpl { | ||
[REFERENCE]; | ||
tag = null; | ||
lastRevision = INITIAL; | ||
lastValue; | ||
children = null; | ||
compute = null; | ||
update = null; | ||
debugLabel; | ||
constructor(type) { | ||
this[REFERENCE] = type; | ||
} | ||
} | ||
function createPrimitiveRef(value) { | ||
const ref = new ReferenceImpl(UNBOUND); | ||
ref.tag = CONSTANT_TAG; | ||
ref.lastValue = value; | ||
{ | ||
ref.debugLabel = String(value); | ||
} | ||
return ref; | ||
} | ||
const UNDEFINED_REFERENCE = createPrimitiveRef(undefined); | ||
const NULL_REFERENCE = createPrimitiveRef(null); | ||
const TRUE_REFERENCE = createPrimitiveRef(true); | ||
const FALSE_REFERENCE = createPrimitiveRef(false); | ||
function createConstRef(value, debugLabel) { | ||
const ref = new ReferenceImpl(CONSTANT); | ||
ref.lastValue = value; | ||
ref.tag = CONSTANT_TAG; | ||
{ | ||
ref.debugLabel = debugLabel; | ||
} | ||
return ref; | ||
} | ||
function createUnboundRef(value, debugLabel) { | ||
const ref = new ReferenceImpl(UNBOUND); | ||
ref.lastValue = value; | ||
ref.tag = CONSTANT_TAG; | ||
{ | ||
ref.debugLabel = debugLabel; | ||
} | ||
return ref; | ||
} | ||
function createComputeRef(compute) { | ||
let update = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
let debugLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'unknown'; | ||
const ref = new ReferenceImpl(COMPUTE); | ||
ref.compute = compute; | ||
ref.update = update; | ||
{ | ||
ref.debugLabel = `(result of a \`${debugLabel}\` helper)`; | ||
} | ||
return ref; | ||
} | ||
function createReadOnlyRef(ref) { | ||
if (!isUpdatableRef(ref)) return ref; | ||
return createComputeRef(() => valueForRef(ref), null, ref.debugLabel); | ||
} | ||
function isInvokableRef(ref) { | ||
return ref[REFERENCE] === INVOKABLE; | ||
} | ||
function createInvokableRef(inner) { | ||
const ref = createComputeRef(() => valueForRef(inner), value => updateRef(inner, value)); | ||
ref.debugLabel = inner.debugLabel; | ||
ref[REFERENCE] = INVOKABLE; | ||
return ref; | ||
} | ||
function isConstRef(_ref) { | ||
const ref = _ref; | ||
return ref.tag === CONSTANT_TAG; | ||
} | ||
function isUpdatableRef(_ref) { | ||
const ref = _ref; | ||
return ref.update !== null; | ||
} | ||
function valueForRef(_ref) { | ||
const ref = _ref; | ||
let { | ||
tag | ||
} = ref; | ||
if (tag === CONSTANT_TAG) { | ||
return ref.lastValue; | ||
} | ||
const { | ||
lastRevision | ||
} = ref; | ||
let lastValue; | ||
if (tag === null || !validateTag(tag, lastRevision)) { | ||
const { | ||
compute | ||
} = ref; | ||
const newTag = track(() => { | ||
lastValue = ref.lastValue = compute(); | ||
}, ref.debugLabel); | ||
tag = ref.tag = newTag; | ||
ref.lastRevision = valueForTag(newTag); | ||
} else { | ||
lastValue = ref.lastValue; | ||
} | ||
consumeTag(tag); | ||
return lastValue; | ||
} | ||
function updateRef(_ref, value) { | ||
const ref = _ref; | ||
const update = expect(ref.update, 'called update on a non-updatable reference'); | ||
update(value); | ||
} | ||
function childRefFor(_parentRef, path) { | ||
const parentRef = _parentRef; | ||
const type = parentRef[REFERENCE]; | ||
let children = parentRef.children; | ||
let child; | ||
if (children === null) { | ||
children = parentRef.children = new Map(); | ||
} else { | ||
child = children.get(path); | ||
if (child !== undefined) { | ||
return child; | ||
} | ||
} | ||
if (type === UNBOUND) { | ||
const parent = valueForRef(parentRef); | ||
if (isDict(parent)) { | ||
child = createUnboundRef(parent[path], `${parentRef.debugLabel}.${path}`); | ||
} else { | ||
child = UNDEFINED_REFERENCE; | ||
} | ||
} else { | ||
child = createComputeRef(() => { | ||
const parent = valueForRef(parentRef); | ||
if (isDict(parent)) { | ||
return getProp(parent, path); | ||
} | ||
}, val => { | ||
const parent = valueForRef(parentRef); | ||
if (isDict(parent)) { | ||
return setProp(parent, path, val); | ||
} | ||
}); | ||
{ | ||
child.debugLabel = `${parentRef.debugLabel}.${path}`; | ||
} | ||
} | ||
children.set(path, child); | ||
return child; | ||
} | ||
function childRefFromParts(root, parts) { | ||
let reference = root; | ||
for (const part of parts) { | ||
reference = childRefFor(reference, part); | ||
} | ||
return reference; | ||
} | ||
let createDebugAliasRef; | ||
{ | ||
createDebugAliasRef = (debugLabel, inner) => { | ||
const update = isUpdatableRef(inner) ? value => updateRef(inner, value) : null; | ||
const ref = createComputeRef(() => valueForRef(inner), update); | ||
ref[REFERENCE] = inner[REFERENCE]; | ||
ref.debugLabel = debugLabel; | ||
return ref; | ||
}; | ||
} | ||
const NULL_IDENTITY = {}; | ||
const KEY = (_, index) => index; | ||
const INDEX = (_, index) => String(index); | ||
const IDENTITY = item => { | ||
if (item === null) { | ||
// Returning null as an identity will cause failures since the iterator | ||
// can't tell that it's actually supposed to be null | ||
return NULL_IDENTITY; | ||
} | ||
return item; | ||
}; | ||
function keyForPath(path) { | ||
if (path[0] === '@') { | ||
throw new Error(`invalid keypath: '${path}', valid keys: @index, @identity, or a path`); | ||
} | ||
return uniqueKeyFor(item => getPath(item, path)); | ||
} | ||
function makeKeyFor(key) { | ||
switch (key) { | ||
case '@key': | ||
return uniqueKeyFor(KEY); | ||
case '@index': | ||
return uniqueKeyFor(INDEX); | ||
case '@identity': | ||
return uniqueKeyFor(IDENTITY); | ||
default: | ||
return keyForPath(key); | ||
} | ||
} | ||
class WeakMapWithPrimitives { | ||
_weakMap; | ||
_primitiveMap; | ||
get weakMap() { | ||
if (this._weakMap === undefined) { | ||
this._weakMap = new WeakMap(); | ||
} | ||
return this._weakMap; | ||
} | ||
get primitiveMap() { | ||
if (this._primitiveMap === undefined) { | ||
this._primitiveMap = new Map(); | ||
} | ||
return this._primitiveMap; | ||
} | ||
set(key, value) { | ||
if (isObject(key)) { | ||
this.weakMap.set(key, value); | ||
} else { | ||
this.primitiveMap.set(key, value); | ||
} | ||
} | ||
get(key) { | ||
if (isObject(key)) { | ||
return this.weakMap.get(key); | ||
} else { | ||
return this.primitiveMap.get(key); | ||
} | ||
} | ||
} | ||
const IDENTITIES = new WeakMapWithPrimitives(); | ||
function identityForNthOccurence(value, count) { | ||
let identities = IDENTITIES.get(value); | ||
if (identities === undefined) { | ||
identities = []; | ||
IDENTITIES.set(value, identities); | ||
} | ||
let identity = identities[count]; | ||
if (identity === undefined) { | ||
identity = { | ||
value, | ||
count | ||
}; | ||
identities[count] = identity; | ||
} | ||
return identity; | ||
} | ||
/** | ||
* When iterating over a list, it's possible that an item with the same unique | ||
* key could be encountered twice: | ||
* | ||
* ```js | ||
* let arr = ['same', 'different', 'same', 'same']; | ||
* ``` | ||
* | ||
* In general, we want to treat these items as _unique within the list_. To do | ||
* this, we track the occurences of every item as we iterate the list, and when | ||
* an item occurs more than once, we generate a new unique key just for that | ||
* item, and that occurence within the list. The next time we iterate the list, | ||
* and encounter an item for the nth time, we can get the _same_ key, and let | ||
* Glimmer know that it should reuse the DOM for the previous nth occurence. | ||
*/ | ||
function uniqueKeyFor(keyFor) { | ||
let seen = new WeakMapWithPrimitives(); | ||
return (value, memo) => { | ||
let key = keyFor(value, memo); | ||
let count = seen.get(key) || 0; | ||
seen.set(key, count + 1); | ||
if (count === 0) { | ||
return key; | ||
} | ||
return identityForNthOccurence(key, count); | ||
}; | ||
} | ||
function createIteratorRef(listRef, key) { | ||
return createComputeRef(() => { | ||
let iterable = valueForRef(listRef); | ||
let keyFor = makeKeyFor(key); | ||
if (Array.isArray(iterable)) { | ||
return new ArrayIterator(iterable, keyFor); | ||
} | ||
let maybeIterator = toIterator(iterable); | ||
if (maybeIterator === null) { | ||
return new ArrayIterator(EMPTY_ARRAY, () => null); | ||
} | ||
return new IteratorWrapper(maybeIterator, keyFor); | ||
}); | ||
} | ||
function createIteratorItemRef(_value) { | ||
let value = _value; | ||
let tag = createTag(); | ||
return createComputeRef(() => { | ||
consumeTag(tag); | ||
return value; | ||
}, newValue => { | ||
if (value !== newValue) { | ||
value = newValue; | ||
dirtyTag(tag); | ||
} | ||
}); | ||
} | ||
class IteratorWrapper { | ||
constructor(inner, keyFor) { | ||
this.inner = inner; | ||
this.keyFor = keyFor; | ||
} | ||
isEmpty() { | ||
return this.inner.isEmpty(); | ||
} | ||
next() { | ||
let nextValue = this.inner.next(); | ||
if (nextValue !== null) { | ||
nextValue.key = this.keyFor(nextValue.value, nextValue.memo); | ||
} | ||
return nextValue; | ||
} | ||
} | ||
class ArrayIterator { | ||
current; | ||
pos = 0; | ||
constructor(iterator, keyFor) { | ||
this.iterator = iterator; | ||
this.keyFor = keyFor; | ||
if (iterator.length === 0) { | ||
this.current = { | ||
kind: 'empty' | ||
}; | ||
} else { | ||
this.current = { | ||
kind: 'first', | ||
value: iterator[this.pos] | ||
}; | ||
} | ||
} | ||
isEmpty() { | ||
return this.current.kind === 'empty'; | ||
} | ||
next() { | ||
let value; | ||
let current = this.current; | ||
if (current.kind === 'first') { | ||
this.current = { | ||
kind: 'progress' | ||
}; | ||
value = current.value; | ||
} else if (this.pos >= this.iterator.length - 1) { | ||
return null; | ||
} else { | ||
value = this.iterator[++this.pos]; | ||
} | ||
let { | ||
keyFor | ||
} = this; | ||
let key = keyFor(value, this.pos); | ||
let memo = this.pos; | ||
return { | ||
key, | ||
value, | ||
memo | ||
}; | ||
} | ||
} | ||
export { FALSE_REFERENCE, NULL_REFERENCE, REFERENCE, TRUE_REFERENCE, UNDEFINED_REFERENCE, childRefFor, childRefFromParts, createComputeRef, createConstRef, createDebugAliasRef, createInvokableRef, createIteratorItemRef, createIteratorRef, createPrimitiveRef, createReadOnlyRef, createUnboundRef, isConstRef, isInvokableRef, isUpdatableRef, updateRef, valueForRef }; | ||
import{getProp as t,setProp as e,toIterator as n,getPath as r}from"@glimmer/global-context";import{expect as i,isDict as u,EMPTY_ARRAY as l,isObject as s}from"@glimmer/util";import{CONSTANT_TAG as a,validateTag as o,track as c,valueForTag as p,consumeTag as d,INITIAL as h,createTag as f,dirtyTag as g}from"@glimmer/validator";const m=Symbol("REFERENCE"),b=1,v=2;class k{[m];tag=null;lastRevision=h;lastValue;children=null;compute=null;update=null;debugLabel;constructor(t){this[m]=t}}function w(t){const e=new k(v);return e.tag=a,e.lastValue=t,e.debugLabel=String(t),e}const y=w(void 0),M=w(null),L=w(!0),E=w(!1);function _(t,e){const n=new k(0);return n.lastValue=t,n.tag=a,n.debugLabel=e,n}function x(t,e){const n=new k(v);return n.lastValue=t,n.tag=a,n.debugLabel=e,n}function V(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"unknown";const r=new k(b);return r.compute=t,r.update=e,r.debugLabel=`(result of a \`${n}\` helper)`,r}function $(t){return A(t)?V((()=>C(t)),null,t.debugLabel):t}function F(t){return 3===t[m]}function R(t){const e=V((()=>C(t)),(e=>N(t,e)));return e.debugLabel=t.debugLabel,e[m]=3,e}function S(t){return t.tag===a}function A(t){return null!==t.update}function C(t){const e=t;let{tag:n}=e;if(n===a)return e.lastValue;const{lastRevision:r}=e;let i;if(null!==n&&o(n,r))i=e.lastValue;else{const{compute:t}=e,r=c((()=>{i=e.lastValue=t()}),e.debugLabel);n=e.tag=r,e.lastRevision=p(r)}return d(n),i}function N(t,e){i(t.update,"called update on a non-updatable reference")(e)}function W(n,r){const i=n,l=i[m];let s,a=i.children;if(null===a)a=i.children=new Map;else if(s=a.get(r),void 0!==s)return s;if(l===v){const t=C(i);s=u(t)?x(t[r],`${i.debugLabel}.${r}`):y}else s=V((()=>{const e=C(i);if(u(e))return t(e,r)}),(t=>{const n=C(i);if(u(n))return e(n,r,t)})),s.debugLabel=`${i.debugLabel}.${r}`;return a.set(r,s),s}function j(t,e){let n=t;for(const t of e)n=W(n,t);return n}let q;q=(t,e)=>{const n=V((()=>C(e)),A(e)?t=>N(e,t):null);return n[m]=e[m],n.debugLabel=t,n};const z={},B=(t,e)=>e,D=(t,e)=>String(e),G=t=>null===t?z:t;function H(t){switch(t){case"@key":return K(B);case"@index":return K(D);case"@identity":return K(G);default:return function(t){if("@"===t[0])throw new Error(`invalid keypath: '${t}', valid keys: @index, @identity, or a path`);return K((e=>r(e,t)))}(t)}}class I{_weakMap;_primitiveMap;get weakMap(){return void 0===this._weakMap&&(this._weakMap=new WeakMap),this._weakMap}get primitiveMap(){return void 0===this._primitiveMap&&(this._primitiveMap=new Map),this._primitiveMap}set(t,e){s(t)?this.weakMap.set(t,e):this.primitiveMap.set(t,e)}get(t){return s(t)?this.weakMap.get(t):this.primitiveMap.get(t)}}const J=new I;function K(t){let e=new I;return(n,r)=>{let i=t(n,r),u=e.get(i)||0;return e.set(i,u+1),0===u?i:function(t,e){let n=J.get(t);void 0===n&&(n=[],J.set(t,n));let r=n[e];return void 0===r&&(r={value:t,count:e},n[e]=r),r}(i,u)}}function O(t,e){return V((()=>{let r=C(t),i=H(e);if(Array.isArray(r))return new T(r,i);let u=n(r);return null===u?new T(l,(()=>null)):new Q(u,i)}))}function P(t){let e=t,n=f();return V((()=>(d(n),e)),(t=>{e!==t&&(e=t,g(n))}))}class Q{constructor(t,e){this.inner=t,this.keyFor=e}isEmpty(){return this.inner.isEmpty()}next(){let t=this.inner.next();return null!==t&&(t.key=this.keyFor(t.value,t.memo)),t}}class T{current;pos=0;constructor(t,e){this.iterator=t,this.keyFor=e,0===t.length?this.current={kind:"empty"}:this.current={kind:"first",value:t[this.pos]}}isEmpty(){return"empty"===this.current.kind}next(){let t,e=this.current;if("first"===e.kind)this.current={kind:"progress"},t=e.value;else{if(this.pos>=this.iterator.length-1)return null;t=this.iterator[++this.pos]}let{keyFor:n}=this;return{key:n(t,this.pos),value:t,memo:this.pos}}}export{E as FALSE_REFERENCE,M as NULL_REFERENCE,m as REFERENCE,L as TRUE_REFERENCE,y as UNDEFINED_REFERENCE,W as childRefFor,j as childRefFromParts,V as createComputeRef,_ as createConstRef,q as createDebugAliasRef,R as createInvokableRef,P as createIteratorItemRef,O as createIteratorRef,w as createPrimitiveRef,$ as createReadOnlyRef,x as createUnboundRef,S as isConstRef,F as isInvokableRef,A as isUpdatableRef,N as updateRef,C as valueForRef}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@glimmer/reference", | ||
"type": "module", | ||
"version": "0.85.5", | ||
"version": "0.85.6", | ||
"description": "Objects used to track values and their dirtiness in Glimmer", | ||
@@ -17,6 +17,6 @@ "license": "MIT", | ||
"@glimmer/env": "^0.1.7", | ||
"@glimmer/global-context": "^0.85.5", | ||
"@glimmer/interfaces": "^0.85.5", | ||
"@glimmer/util": "^0.85.5", | ||
"@glimmer/validator": "^0.85.5" | ||
"@glimmer/global-context": "^0.85.6", | ||
"@glimmer/interfaces": "^0.85.6", | ||
"@glimmer/util": "^0.85.6", | ||
"@glimmer/validator": "^0.85.6" | ||
}, | ||
@@ -27,3 +27,3 @@ "devDependencies": { | ||
"publint": "^0.2.5", | ||
"@glimmer/local-debug-flags": "^0.85.5", | ||
"@glimmer/local-debug-flags": "^0.85.6", | ||
"@glimmer-workspace/build-support": "^1.0.0" | ||
@@ -30,0 +30,0 @@ }, |
Sorry, the diff of this file is not supported yet
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
44380
-21.59%545
-39.11%3
50%Updated
Updated
Updated