@dark-engine/core
Version:
The lightweight and powerful UI rendering engine without dependencies and written in TypeScript (Browser, Node.js, Android, iOS, Windows, Linux, macOS)
321 lines (320 loc) • 7.21 kB
JavaScript
import { platform, detectIsServer } from '../platform';
import { Reconciler } from '../reconciler';
import { EventEmitter } from '../emitter';
import { Awaiter } from '../awaiter';
class Scope {
root = null;
wip = null;
cursor = null;
unit = null;
mDeep = true;
mLevel = 0;
mNav = new Map();
events = new Map();
offs = new Set();
reconciler = new Reconciler();
candidates = new Set();
deletions = new Set();
cancels = [];
asyncEffects = new Set();
layoutEffects = new Set();
insertionEffects = new Set();
resId = 0;
resources = new Map();
awaiter = new Awaiter();
onTransitionEnd = null;
isLayoutEffect = false;
isInsertionEffect = false;
isUpdate = false;
isBatch = false;
isForce = false;
isHydration = false;
isStream = false;
isTransition = false;
isEvent = false;
isHot = false;
isDynamic = platform.detectIsDynamic();
isServer = detectIsServer();
emitter = new EventEmitter();
fork() {
const scope = new Scope();
scope.root = null;
scope.wip = null;
scope.cursor = null;
scope.unit = this.unit;
scope.mDeep = this.mDeep;
scope.mLevel = this.mLevel;
scope.mNav = new Map(this.mNav);
scope.events = this.events;
scope.offs = this.offs;
scope.reconciler = this.reconciler.fork();
scope.candidates = new Set([...this.candidates]);
scope.deletions = new Set([...this.deletions]);
scope.asyncEffects = new Set([...this.asyncEffects]);
scope.layoutEffects = new Set([...this.layoutEffects]);
scope.isUpdate = this.isUpdate;
scope.emitter = this.emitter;
scope.awaiter = this.awaiter;
return scope;
}
getRoot() {
return this.root;
}
setRoot(fiber) {
this.root = fiber;
}
keepRoot() {
!this.isUpdate && this.setRoot(this.wip);
}
getWorkInProgress() {
return this.wip;
}
setWorkInProgress(fiber) {
this.wip = fiber;
}
getNextUnitOfWork() {
return this.unit;
}
setUnitOfWork(fiber) {
this.unit = fiber;
}
getCursor() {
return this.cursor;
}
setCursor(fiber) {
this.cursor = fiber;
}
navToChild() {
this.mLevel = this.mLevel + 1;
this.mNav.set(this.mLevel, 0);
}
navToSibling() {
this.mNav.set(this.mLevel, this.mNav.get(this.mLevel) + 1);
}
navToParent() {
this.mLevel = this.mLevel - 1;
}
navToPrev() {
const idx = this.getMountIndex();
if (idx === 0) {
this.navToParent();
this.setMountDeep(true);
} else {
this.mNav.set(this.mLevel, this.mNav.get(this.mLevel) - 1);
this.setMountDeep(false);
}
}
getMountIndex() {
return this.mNav.get(this.mLevel);
}
getMountDeep() {
return this.mDeep;
}
setMountDeep(value) {
this.mDeep = value;
}
resetMount() {
this.mLevel = 0;
this.mNav = new Map();
this.mDeep = true;
}
getEvents() {
return this.events;
}
addOff(fn) {
this.offs.add(fn);
}
off() {
this.offs.forEach(x => x());
this.offs = new Set();
}
getCandidates() {
return this.candidates;
}
addCandidate(fiber) {
this.candidates.add(fiber);
}
resetCandidates() {
this.candidates = new Set();
}
getDeletions() {
return this.deletions;
}
hasDeletion(fiber) {
let nextFiber = fiber;
while (nextFiber) {
if (this.deletions.has(nextFiber)) return true;
nextFiber = nextFiber.parent;
}
return false;
}
addDeletion(fiber) {
!this.hasDeletion(fiber) && this.deletions.add(fiber);
}
resetDeletions() {
this.deletions = new Set();
}
addAsyncEffect(fn) {
this.asyncEffects.add(fn);
}
resetAsyncEffects() {
this.asyncEffects = new Set();
}
runAsyncEffects() {
if (!this.isDynamic) return;
const effects = this.asyncEffects;
effects.size > 0 && setTimeout(() => effects.forEach(fn => fn()));
}
addLayoutEffect(fn) {
this.layoutEffects.add(fn);
}
resetLayoutEffects() {
this.layoutEffects = new Set();
}
runLayoutEffects() {
if (!this.isDynamic) return;
this.setIsLayoutEffect(true);
this.layoutEffects.forEach(fn => fn());
this.setIsLayoutEffect(false);
}
addInsertionEffect(fn) {
this.insertionEffects.add(fn);
}
resetInsertionEffects() {
this.insertionEffects = new Set();
}
runInsertionEffects() {
if (!this.isDynamic) return;
this.setIsInsertionEffect(true);
this.insertionEffects.forEach(fn => fn());
this.setIsInsertionEffect(false);
}
addCancel(fn) {
this.cancels.push(fn);
}
applyCancels() {
for (let i = this.cancels.length - 1; i >= 0; i--) {
this.cancels[i]();
}
}
resetCancels() {
this.cancels = [];
}
getIsLayoutEffect() {
return this.isLayoutEffect;
}
setIsLayoutEffect(value) {
this.isLayoutEffect = value;
}
getIsInsertionEffect() {
return this.isInsertionEffect;
}
setIsInsertionEffect(value) {
this.isInsertionEffect = value;
}
getIsUpdate() {
return this.isUpdate;
}
setIsUpdate(value) {
this.isUpdate = value;
}
getIsBatch() {
return this.isBatch;
}
setIsBatch(value) {
this.isBatch = value;
}
getIsHydration() {
return this.isHydration;
}
setIsHydration(value) {
this.isHydration = value;
}
getIsStream() {
return this.isStream;
}
setIsStream(value) {
this.isStream = value;
}
getIsTransition() {
return this.isTransition;
}
setIsTransition(value) {
this.isTransition = value;
}
getIsEvent() {
return this.isEvent;
}
setIsEvent(value, isForce = false) {
this.isEvent = value;
this.isForce = isForce;
}
getIsForce() {
return this.isForce;
}
getIsHot() {
return this.isHot;
}
setIsHot(value) {
this.isHot = value;
}
getOnTransitionEnd() {
return this.onTransitionEnd;
}
setOnTransitionEnd(fn) {
this.onTransitionEnd = fn;
}
getReconciler() {
return this.reconciler;
}
cleanup() {
this.keepRoot();
this.setWorkInProgress(null);
this.setUnitOfWork(null);
this.setCursor(null);
this.resetMount();
this.resetCandidates();
this.resetDeletions();
this.resetCancels();
this.resetInsertionEffects();
this.resetLayoutEffects();
this.resetAsyncEffects();
this.setIsHydration(false);
this.setIsUpdate(false);
this.reconciler.reset();
}
getEmitter() {
return this.emitter;
}
getResource(id) {
return this.resources.get(id);
}
setResource(id, res) {
this.resources.set(id, res);
}
getResources() {
return this.resources;
}
getNextResourceId() {
return ++this.resId;
}
getAwaiter() {
return this.awaiter;
}
runAfterCommit() {
this.resources = new Map();
this.isServer && (this.resId = 0);
}
}
let rootId = null;
const scopes = new Map();
const getRootId = () => rootId;
const setRootId = id => {
rootId = id;
!scopes.has(rootId) && scopes.set(rootId, new Scope());
};
const removeScope = id => scopes.delete(id);
const replaceScope = (scope, id = rootId) => Object.assign(scopes.get(id), scope);
const $$scope = (id = rootId) => scopes.get(id);
export { getRootId, setRootId, removeScope, replaceScope, $$scope };
//# sourceMappingURL=scope.js.map