@glimmer/reference
Advanced tools
Comparing version 0.85.5 to 0.85.6
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44380
545
Updated@glimmer/interfaces@^0.85.6
Updated@glimmer/util@^0.85.6
Updated@glimmer/validator@^0.85.6