UNPKG

@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)

203 lines (202 loc) 6.76 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.notifyParents = exports.tryOptMemoSlot = exports.tryOptStaticSlot = exports.createLoc = exports.createHookLoc = exports.resolveBoundary = exports.resolveSuspense = exports.detectIsFiberAlive = exports.getFiberWithElement = exports.collectElements = exports.walk = void 0; const constants_1 = require('../constants'); const view_1 = require('../view'); const fiber_1 = require('../fiber'); const utils_1 = require('../utils'); const memo_1 = require('../memo'); function walk(fiber, onWalk) { let shouldDeep = true; let shouldStop = false; const skip = () => (shouldDeep = false); const stop = () => (shouldStop = true); const stack = [fiber]; while (stack.length !== 0) { const unit = stack.pop(); onWalk(unit, skip, stop); if (shouldStop) break; unit !== fiber && unit.next && stack.push(unit.next); shouldDeep && unit.child && stack.push(unit.child); shouldDeep = true; } } exports.walk = walk; function collectElements(fiber, transform) { const elements = []; walk(fiber, onWalkInCollectElements(elements, transform)); return elements; } exports.collectElements = collectElements; function onWalkInCollectElements(elements, transform) { return (fiber, skip) => { if (fiber.el) { !fiber.hook?.getIsPortal() && elements.push(transform(fiber)); return skip(); } }; } function getFiberWithElement(fiber) { let $fiber = fiber; while ($fiber) { if ($fiber.el) return $fiber; $fiber = $fiber.parent; } return $fiber; } exports.getFiberWithElement = getFiberWithElement; function detectIsFiberAlive(fiber) { let $fiber = fiber; while ($fiber) { if ($fiber.tag === constants_1.DELETE_EFFECT_TAG) return false; $fiber = $fiber.parent; } return Boolean(fiber); } exports.detectIsFiberAlive = detectIsFiberAlive; function getSuspense(fiber, isPending) { let suspense = fiber; while (suspense) { if (suspense.hook?.getIsSuspense() && (isPending ? suspense.hook.getIsPending() : true)) return suspense; suspense = suspense.parent; } return null; } function resolveSuspense(fiber) { return getSuspense(fiber, true) || getSuspense(fiber, false) || null; } exports.resolveSuspense = resolveSuspense; function resolveBoundary(fiber) { let boundary = fiber; while (boundary) { if (boundary.hook?.getIsBoundary()) return boundary; boundary = boundary.parent; } return null; } exports.resolveBoundary = resolveBoundary; function createHookLoc(rootId, idx, hook) { const fiber = hook.owner; let $fiber = fiber; let loc = `${fiber.idx}${constants_1.HOOK_DELIMETER}${idx}`; while ($fiber) { $fiber = $fiber.parent; $fiber && (loc = `${$fiber.idx}.${loc}`); } loc = `[${rootId}]${loc}`; return loc; } exports.createHookLoc = createHookLoc; const createLoc = (rootId, idx, hook) => () => createHookLoc(rootId, idx, hook); exports.createLoc = createLoc; function detectIsStableMemoTree(fiber, $scope) { if (!(0, view_1.hasChildrenProp)(fiber.inst)) return; const store = $scope.getReconciler().get(fiber.id); const children = fiber.inst.children; for (let i = 0; i < children.length; i++) { const inst = children[i]; const key = (0, view_1.getElementKey)(inst); if (key === null) return false; const alt = store.map[key]; if (!alt) return false; const pc = alt.inst; const nc = inst; const isStable = (0, memo_1.detectIsMemo)(nc) && (0, memo_1.detectIsMemo)(pc) && nc.type === pc.type && !nc.shouldUpdate(pc.props, nc.props); if (!isStable) return false; } return true; } function tryOptStaticSlot(fiber, alt, $scope) { const store = $scope.getReconciler().get(fiber.id); const inst = fiber.inst; alt.el && (fiber.el = alt.el); for (let i = 0; i < inst.children.length; i++) { buildChildNode(inst.children, fiber, store.map, i, fiber.eidx); } fiber.cc = inst.children.length; $scope.setMountDeep(false); } exports.tryOptStaticSlot = tryOptStaticSlot; function tryOptMemoSlot(fiber, alt, $scope) { const store = $scope.getReconciler().get(fiber.id); const hasMove = Boolean(store.move); const hasRemove = Boolean(store.remove); const hasInsert = Boolean(store.insert); const hasReplace = Boolean(store.replace); const canOptimize = ((hasMove && !hasRemove) || (hasRemove && !hasMove)) && !hasInsert && !hasReplace; if (!canOptimize || !detectIsStableMemoTree(fiber, $scope)) return; hasMove && tryOptMov(fiber, alt, $scope); hasRemove && buildChildNodes(fiber, alt, $scope); } exports.tryOptMemoSlot = tryOptMemoSlot; function tryOptMov(fiber, alt, $scope) { const store = $scope.getReconciler().get(fiber.id); buildChildNodes(fiber, alt, $scope, (fiber, key) => { if (!store.move[key]) return; fiber.alt = new fiber_1.Fiber().mutate(fiber); fiber.tag = constants_1.UPDATE_EFFECT_TAG; fiber.mask |= constants_1.MOVE_MASK; $scope.addCandidate(fiber); }); } function buildChildNodes(fiber, alt, $scope, onNode) { const store = $scope.getReconciler().get(fiber.id); const inst = fiber.inst; const children = inst.children; alt.el && (fiber.el = alt.el); for (let i = 0; i < children.length; i++) { const key = getKey(children[i], i); const $fiber = store.map[key]; buildChildNode(children, fiber, store.map, i, fiber.eidx); onNode && onNode($fiber, key); } fiber.cc = children.length; $scope.setMountDeep(false); } function buildChildNode(children, parent, altMap, idx, startEidx) { const prevIdx = idx - 1; const nextIdx = idx + 1; const key = getKey(children[idx], idx); const prevKey = getKey(children[prevIdx], prevIdx); const nextKey = getKey(children[nextIdx], nextIdx); const fiber = altMap[key]; const left = altMap[prevKey]; const right = altMap[nextKey]; const isFirst = idx === 0; const isLast = idx === children.length - 1; isFirst && (parent.child = fiber); fiber.alt = null; fiber.parent = parent; fiber.tag = constants_1.SKIP_EFFECT_TAG; fiber.idx = idx; left ? (fiber.eidx = left.eidx + (left.el ? 1 : left.cec)) : (fiber.eidx = startEidx); right && (fiber.next = right); isLast && (fiber.next = null); notifyParents(fiber); } function getKey(inst, idx) { const key = (0, view_1.getElementKey)(inst); return key !== null ? key : (0, utils_1.createIndexKey)(idx); } function notifyParents(fiber, alt = fiber) { fiber.increment(alt.el ? 1 : alt.cec); alt.mask & constants_1.EFFECT_HOST_MASK && fiber.markHost(constants_1.EFFECT_HOST_MASK); alt.mask & constants_1.ATOM_HOST_MASK && fiber.markHost(constants_1.ATOM_HOST_MASK); } exports.notifyParents = notifyParents; //# sourceMappingURL=walk.js.map