From dd184fb6f6d34326ded0a04d7b3ea556bacef5fe Mon Sep 17 00:00:00 2001 From: Alex Kanunnikov Date: Tue, 6 Feb 2024 21:58:41 +0300 Subject: [PATCH] glimmer validator compat (#95) * glimmer-validator-compat * + --- src/utils/glimmer-compat.ts | 4 ++ src/utils/glimmer-validator.ts | 92 ++++++++++++++++++++++++++++++++++ src/utils/reactive.ts | 33 +++++++----- 3 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 src/utils/glimmer-compat.ts create mode 100644 src/utils/glimmer-validator.ts diff --git a/src/utils/glimmer-compat.ts b/src/utils/glimmer-compat.ts new file mode 100644 index 00000000..bdcb1408 --- /dev/null +++ b/src/utils/glimmer-compat.ts @@ -0,0 +1,4 @@ +export * as caching from './caching-primitives'; +export * as destroyable from './destroyable'; +export * as storage from './storage-primitives'; +export * as validator from './glimmer-validator'; \ No newline at end of file diff --git a/src/utils/glimmer-validator.ts b/src/utils/glimmer-validator.ts new file mode 100644 index 00000000..7c38395c --- /dev/null +++ b/src/utils/glimmer-validator.ts @@ -0,0 +1,92 @@ +import { + type Cell, + cellFor, + cellsMap, + getTracker, + isRendering, + setIsRendering, +} from './reactive'; + +export { cellFor as tagFor } from '@lifeart/gxt'; + +export function dirtyTagFor(obj: object, key: string | number | symbol): void { + // @ts-expect-error + const cell = cellFor(obj, key); + cell.update(cell.value); +} +export function tagMetaFor(obj: object): any { + return cellsMap.get(obj); +} + +export function isTracking(): boolean { + return getTracker() !== null; +} + +export function consumeTag(tag: Cell): void { + const TRACKER = getTracker(); + if (TRACKER !== null) { + TRACKER.add(tag); + } +} + +export type Getter = (self: T) => T[K] | undefined; +export type Setter = (self: T, value: T[K]) => void; + +export function trackedData( + key: K, + initializer?: (this: T) => T[K], +): { getter: Getter; setter: Setter } { + let values = new WeakMap(); + let hasInitializer = typeof initializer === 'function'; + + function getter(self: T) { + consumeTag(cellFor(self, key)); + + let value; + + // If the field has never been initialized, we should initialize it + if (hasInitializer && !values.has(self)) { + value = initializer!.call(self); + values.set(self, value); + } else { + value = values.get(self); + } + + return value; + } + + function setter(self: T, value: T[K]): void { + dirtyTagFor(self, key); + values.set(self, value); + } + + return { getter, setter }; +} + +let renderingStateBeforeBegin = isRendering(); + +export function beginTrackFrame() { + renderingStateBeforeBegin = isRendering(); + if (!isRendering()) { + setIsRendering(true); + } +} + +export function endTrackFrame() { + if (isRendering() !== renderingStateBeforeBegin) { + setIsRendering(renderingStateBeforeBegin); + } +} + +export function beginUntrackFrame() { + renderingStateBeforeBegin = isRendering(); + if (renderingStateBeforeBegin) { + setIsRendering(false); + } +} + +export function endUntrackFrame() { + if (isRendering() !== renderingStateBeforeBegin) { + setIsRendering(renderingStateBeforeBegin); + } +} diff --git a/src/utils/reactive.ts b/src/utils/reactive.ts index 5a38f498..10c9ee74 100644 --- a/src/utils/reactive.ts +++ b/src/utils/reactive.ts @@ -21,7 +21,7 @@ export const DEBUG_MERGED_CELLS = new Set(); export const DEBUG_CELLS = new Set(); var currentTracker: Set | null = null; let _isRendering = false; -const cellsMap = new WeakMap>>(); +export const cellsMap = new WeakMap>>(); export function getCells() { return Array.from(DEBUG_CELLS); @@ -40,9 +40,9 @@ if (IS_DEV_MODE) { } } -function keysFor(obj: object): Record> { +function keysFor(obj: object): Map> { if (!cellsMap.has(obj)) { - cellsMap.set(obj, {}); + cellsMap.set(obj, new Map()); } return cellsMap.get(obj)!; } @@ -56,26 +56,26 @@ export function tracked( return { get() { const keys = keysFor(this); - if (!(key in keys)) { + if (!keys.has(key)) { const value: any = cell( hasInitializer ? descriptor!.initializer?.call(this) : descriptor?.value, `${klass.constructor.name}.${key}.@tracked`, ); - keys[key] = value; + keys.set(key, value); return value.value; } else { - return keys[key].value; + return keys.get(key)!.value; } }, set(newValue: any) { const keys = keysFor(this); - if (!(key in keys)) { - keys[key] = cell(newValue, `${klass.constructor.name}.${key}.@tracked`); + if (!keys.has(key)) { + keys.set(key, cell(newValue, `${klass.constructor.name}.${key}.@tracked`)); return; } - const _cell = keys[key]; + const _cell = keys.get(key)!; if (_cell.value === newValue) { return; } @@ -278,15 +278,15 @@ export function cellFor( obj: T, key: K, ): Cell { - const refs = cellsMap.get(obj) || {}; - if (key in refs) { - return refs[key as unknown as string] as Cell; + const refs = cellsMap.get(obj) || new Map(); + if (refs.has(key)) { + return refs.get(key) as Cell; } const cellValue = new Cell( obj[key], `${obj.constructor.name}.${String(key)}`, ); - refs[key as unknown as string] = cellValue; + refs.set(key, cellValue); cellsMap.set(obj, refs); Object.defineProperty(obj, key, { get() { @@ -326,3 +326,10 @@ export function inNewTrackingFrame(callback: () => void) { callback(); currentTracker = existingTracker; } + +export function getTracker() { + return currentTracker; +} +export function setTracker(tracker: Set | null) { + currentTracker = tracker; +} \ No newline at end of file