refactor: make portal tree-shakeable

This commit is contained in:
Evan You 2020-02-15 11:40:09 -05:00
parent 0fc720c34a
commit 9d2ac6675a
14 changed files with 293 additions and 240 deletions

View File

@ -12,7 +12,7 @@ import {
TestElement,
TestNode
} from '@vue/runtime-test'
import { VNodeArrayChildren } from '../src/vnode'
import { VNodeArrayChildren } from '../../src/vnode'
describe('renderer: portal', () => {
test('should work', () => {

View File

@ -141,19 +141,18 @@ export function createAppAPI<HostNode, HostElement>(
},
mixin(mixin: ComponentOptions) {
if (__DEV__ && !__FEATURE_OPTIONS__) {
if (__FEATURE_OPTIONS__) {
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
} else if (__DEV__) {
warn(
'Mixin has already been applied to target app' +
(mixin.name ? `: ${mixin.name}` : '')
)
}
} else if (__DEV__) {
warn('Mixins are only available in builds supporting Options API')
}
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
} else if (__DEV__) {
warn(
'Mixin has already been applied to target app' +
(mixin.name ? `: ${mixin.name}` : '')
)
}
return app
},

View File

@ -72,9 +72,9 @@ const KeepAliveImpl = {
const sink = instance.sink as KeepAliveSink
const {
renderer: {
move,
unmount: _unmount,
options: { createElement }
m: move,
um: _unmount,
o: { createElement }
},
parentSuspense
} = sink

View File

@ -0,0 +1,118 @@
import { ComponentInternalInstance } from '../component'
import { SuspenseBoundary } from './Suspense'
import { RendererInternals, MoveType } from '../renderer'
import { VNode, VNodeArrayChildren } from '../vnode'
import { isString, ShapeFlags, PatchFlags } from '@vue/shared'
import { warn } from '../warning'
export const isPortal = (type: any): boolean => type.__isPortal
export interface PortalProps {
target: string | object
}
export const PortalImpl = {
__isPortal: true,
process(
n1: VNode | null,
n2: VNode,
container: object,
anchor: object | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
optimized: boolean,
{
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
m: move,
c: insertComment,
o: { querySelector, setElementText }
}: RendererInternals
) {
const targetSelector = n2.props && n2.props.target
const { patchFlag, shapeFlag, children } = n2
if (n1 == null) {
if (__DEV__ && isString(targetSelector) && !querySelector) {
warn(
`Current renderer does not support string target for Portals. ` +
`(missing querySelector renderer option)`
)
}
const target = (n2.target = isString(targetSelector)
? querySelector!(targetSelector)
: targetSelector)
if (target != null) {
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(target, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
children as VNodeArrayChildren,
target,
null,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else if (__DEV__) {
warn('Invalid Portal target on mount:', target, `(${typeof target})`)
}
} else {
// update content
const target = (n2.target = n1.target)!
if (patchFlag === PatchFlags.TEXT) {
setElementText(target, children as string)
} else if (n2.dynamicChildren) {
// fast path when the portal happens to be a block root
patchBlockChildren(
n1.dynamicChildren!,
n2.dynamicChildren,
container,
parentComponent,
parentSuspense,
isSVG
)
} else if (!optimized) {
patchChildren(
n1,
n2,
target,
null,
parentComponent,
parentSuspense,
isSVG
)
}
// target changed
if (targetSelector !== (n1.props && n1.props.target)) {
const nextTarget = (n2.target = isString(targetSelector)
? querySelector!(targetSelector)
: targetSelector)
if (nextTarget != null) {
// move content
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(target, '')
setElementText(nextTarget, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNode[]).length; i++) {
move((children as VNode[])[i], nextTarget, null, MoveType.REORDER)
}
}
} else if (__DEV__) {
warn('Invalid Portal target on update:', target, `(${typeof target})`)
}
}
}
// insert an empty node as the placeholder for the portal
insertComment(n1, n2, container, anchor)
}
}
// Force-casted public typing for h and TSX props inference
export const Portal = (PortalImpl as any) as {
__isPortal: true
new (): { $props: PortalProps }
}

View File

@ -13,6 +13,8 @@ export interface SuspenseProps {
onRecede?: () => void
}
export const isSuspense = (type: any): boolean => type.__isSuspense
// Suspense exposes a component-like API, and is treated like a component
// in the compiler, but internally it's a special built-in type that hooks
// directly into the renderer.
@ -79,8 +81,8 @@ function mountSuspense(
rendererInternals: RendererInternals
) {
const {
patch,
options: { createElement }
p: patch,
o: { createElement }
} = rendererInternals
const hiddenContainer = createElement('div')
const suspense = (n2.suspense = createSuspenseBoundary(
@ -138,7 +140,7 @@ function patchSuspense(
parentComponent: ComponentInternalInstance | null,
isSVG: boolean,
optimized: boolean,
{ patch }: RendererInternals
{ p: patch }: RendererInternals
) {
const suspense = (n2.suspense = n1.suspense)!
suspense.vnode = n2
@ -236,11 +238,11 @@ function createSuspenseBoundary<HostNode, HostElement>(
rendererInternals: RendererInternals<HostNode, HostElement>
): SuspenseBoundary<HostNode, HostElement> {
const {
patch,
move,
unmount,
next,
options: { parentNode }
p: patch,
m: move,
um: unmount,
n: next,
o: { parentNode }
} = rendererInternals
const suspense: SuspenseBoundary<HostNode, HostElement> = {

View File

@ -4,9 +4,9 @@ import {
createVNode,
VNodeArrayChildren,
Fragment,
Portal,
isVNode
} from './vnode'
import { Portal, PortalProps } from './components/Portal'
import { Suspense, SuspenseProps } from './components/Suspense'
import { isObject, isArray } from '@vue/shared'
import { RawSlots } from './componentSlots'
@ -95,7 +95,7 @@ export function h(
// portal (target prop is required)
export function h(
type: typeof Portal,
props: RawProps & { target: any },
props: RawProps & PortalProps,
children: RawChildren
): VNode

View File

@ -1,12 +1,4 @@
import {
VNode,
normalizeVNode,
Text,
Comment,
Static,
Fragment,
Portal
} from './vnode'
import { VNode, normalizeVNode, Text, Comment, Static, Fragment } from './vnode'
import { queuePostFlushCb, flushPostFlushCbs } from './scheduler'
import { ComponentInternalInstance } from './component'
import { invokeDirectiveHook } from './directives'
@ -18,7 +10,7 @@ import {
isOn,
isString
} from '@vue/shared'
import { RendererOptions, MountComponentFn } from './renderer'
import { RendererInternals } from './renderer'
export type RootHydrateFunction = (
vnode: VNode<Node, Element>,
@ -30,10 +22,10 @@ export type RootHydrateFunction = (
// it out creates a ton of unnecessary complexity.
// Hydration also depends on some renderer internal logic which needs to be
// passed in via arguments.
export function createHydrationFunctions(
mountComponent: MountComponentFn<Node, Element>,
patchProp: RendererOptions['patchProp']
) {
export function createHydrationFunctions({
mt: mountComponent,
o: { patchProp }
}: RendererInternals<Node, Element>) {
const hydrate: RootHydrateFunction = (vnode, container) => {
if (__DEV__ && !container.hasChildNodes()) {
warn(`Attempting to hydrate existing markup but container is empty.`)
@ -65,9 +57,6 @@ export function createHydrationFunctions(
// TODO handle potential hydration error if fragment didn't get
// back anchor as expected.
return anchor.nextSibling
case Portal:
hydratePortal(vnode, parentComponent)
return node.nextSibling
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
return hydrateElement(node as Element, vnode, parentComponent)
@ -78,6 +67,9 @@ export function createHydrationFunctions(
mountComponent(vnode, null, null, parentComponent, null, false)
const subTree = vnode.component!.subTree
return (subTree.anchor || subTree.el).nextSibling
} else if (shapeFlag & ShapeFlags.PORTAL) {
hydratePortal(vnode, parentComponent)
return node.nextSibling
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
// TODO Suspense
} else if (__DEV__) {

View File

@ -49,7 +49,8 @@ export {
createBlock
} from './vnode'
// Internal Components
export { Text, Comment, Fragment, Portal } from './vnode'
export { Text, Comment, Fragment } from './vnode'
export { Portal, PortalProps } from './components/Portal'
export { Suspense, SuspenseProps } from './components/Suspense'
export { KeepAlive, KeepAliveProps } from './components/KeepAlive'
export {

View File

@ -2,7 +2,6 @@ import {
Text,
Fragment,
Comment,
Portal,
cloneIfMounted,
normalizeVNode,
VNode,
@ -59,9 +58,10 @@ import {
queueEffectWithSuspense,
SuspenseImpl
} from './components/Suspense'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { PortalImpl } from './components/Portal'
import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
const __HMR__ = __BUNDLER__ && __DEV__
@ -113,13 +113,19 @@ export interface RendererOptions<HostNode = any, HostElement = any> {
}
// An object exposing the internals of a renderer, passed to tree-shakeable
// features so that they can be decoupled from this file.
// features so that they can be decoupled from this file. Keys are shortened
// to optimize bundle size.
export interface RendererInternals<HostNode = any, HostElement = any> {
patch: PatchFn<HostNode, HostElement>
unmount: UnmountFn<HostNode, HostElement>
move: MoveFn<HostNode, HostElement>
next: NextFn<HostNode, HostElement>
options: RendererOptions<HostNode, HostElement>
p: PatchFn<HostNode, HostElement>
um: UnmountFn<HostNode, HostElement>
m: MoveFn<HostNode, HostElement>
mt: MountComponentFn<HostNode, HostElement>
mc: MountChildrenFn<HostNode, HostElement>
pc: PatchChildrenFn<HostNode, HostElement>
pbc: PatchBlockChildrenFn<HostNode, HostElement>
n: NextFn<HostNode, HostElement>
o: RendererOptions<HostNode, HostElement>
c: ProcessTextOrCommentFn<HostNode, HostElement>
}
// These functions are created inside a closure and therefore there types cannot
@ -136,11 +142,35 @@ type PatchFn<HostNode, HostElement> = (
optimized?: boolean
) => void
type UnmountFn<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement>,
type MountChildrenFn<HostNode, HostElement> = (
children: VNodeArrayChildren<HostNode, HostElement>,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
doRemove?: boolean
isSVG: boolean,
optimized: boolean,
start?: number
) => void
type PatchChildrenFn<HostNode, HostElement> = (
n1: VNode<HostNode, HostElement> | null,
n2: VNode<HostNode, HostElement>,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
isSVG: boolean,
optimized?: boolean
) => void
type PatchBlockChildrenFn<HostNode, HostElement> = (
oldChildren: VNode<HostNode, HostElement>[],
newChildren: VNode<HostNode, HostElement>[],
fallbackContainer: HostElement,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
isSVG: boolean
) => void
type MoveFn<HostNode, HostElement> = (
@ -155,6 +185,13 @@ type NextFn<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement>
) => HostNode | null
type UnmountFn<HostNode, HostElement> = (
vnode: VNode<HostNode, HostElement>,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
doRemove?: boolean
) => void
type UnmountChildrenFn<HostNode, HostElement> = (
children: VNode<HostNode, HostElement>[],
parentComponent: ComponentInternalInstance | null,
@ -172,6 +209,13 @@ export type MountComponentFn<HostNode, HostElement> = (
isSVG: boolean
) => void
type ProcessTextOrCommentFn<HostNode, HostElement> = (
n1: VNode<HostNode, HostElement> | null,
n2: VNode<HostNode, HostElement>,
container: HostElement,
anchor: HostNode | null
) => void
export type SetupRenderEffectFn<HostNode, HostElement> = (
instance: ComponentInternalInstance,
initialVNode: VNode<HostNode, HostElement>,
@ -282,7 +326,6 @@ function baseCreateRenderer<
setElementText: hostSetElementText,
parentNode: hostParentNode,
nextSibling: hostNextSibling,
querySelector: hostQuerySelector,
setScopeId: hostSetScopeId = NOOP,
cloneNode: hostCloneNode,
insertStaticContent: hostInsertStaticContent
@ -332,18 +375,6 @@ function baseCreateRenderer<
optimized
)
break
case Portal:
processPortal(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
@ -367,6 +398,18 @@ function baseCreateRenderer<
isSVG,
optimized
)
} else if (shapeFlag & ShapeFlags.PORTAL) {
;(type as typeof PortalImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized,
internals
)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
@ -385,11 +428,11 @@ function baseCreateRenderer<
}
}
const processText = (
n1: HostVNode | null,
n2: HostVNode,
container: HostElement,
anchor: HostNode | null
const processText: ProcessTextOrCommentFn<HostNode, HostElement> = (
n1,
n2,
container,
anchor
) => {
if (n1 == null) {
hostInsert(
@ -405,11 +448,11 @@ function baseCreateRenderer<
}
}
const processCommentNode = (
n1: HostVNode | null,
n2: HostVNode,
container: HostElement,
anchor: HostNode | null
const processCommentNode: ProcessTextOrCommentFn<HostNode, HostElement> = (
n1,
n2,
container,
anchor
) => {
if (n1 == null) {
hostInsert(
@ -552,15 +595,15 @@ function baseCreateRenderer<
}
}
const mountChildren = (
children: HostVNodeChildren,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean,
optimized: boolean,
start: number = 0
const mountChildren: MountChildrenFn<HostNode, HostElement> = (
children,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized,
start = 0
) => {
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
@ -716,13 +759,13 @@ function baseCreateRenderer<
}
// The fast path for blocks.
const patchBlockChildren = (
oldChildren: HostVNode[],
newChildren: HostVNode[],
fallbackContainer: HostElement,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean
const patchBlockChildren: PatchBlockChildrenFn<HostNode, HostElement> = (
oldChildren,
newChildren,
fallbackContainer,
parentComponent,
parentSuspense,
isSVG
) => {
for (let i = 0; i < newChildren.length; i++) {
const oldVNode = oldChildren[i]
@ -883,100 +926,6 @@ function baseCreateRenderer<
}
}
const processPortal = (
n1: HostVNode | null,
n2: HostVNode,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean,
optimized: boolean
) => {
const targetSelector = n2.props && n2.props.target
const { patchFlag, shapeFlag, children } = n2
if (n1 == null) {
if (__DEV__ && isString(targetSelector) && !hostQuerySelector) {
warn(
`Current renderer does not support string target for Portals. ` +
`(missing querySelector renderer option)`
)
}
const target = (n2.target = isString(targetSelector)
? hostQuerySelector!(targetSelector)
: targetSelector)
if (target != null) {
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(target, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
children as HostVNodeChildren,
target,
null,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else if (__DEV__) {
warn('Invalid Portal target on mount:', target, `(${typeof target})`)
}
} else {
// update content
const target = (n2.target = n1.target)!
if (patchFlag === PatchFlags.TEXT) {
hostSetElementText(target, children as string)
} else if (n2.dynamicChildren) {
// fast path when the portal happens to be a block root
patchBlockChildren(
n1.dynamicChildren!,
n2.dynamicChildren,
container,
parentComponent,
parentSuspense,
isSVG
)
} else if (!optimized) {
patchChildren(
n1,
n2,
target,
null,
parentComponent,
parentSuspense,
isSVG
)
}
// target changed
if (targetSelector !== (n1.props && n1.props.target)) {
const nextTarget = (n2.target = isString(targetSelector)
? hostQuerySelector!(targetSelector)
: targetSelector)
if (nextTarget != null) {
// move content
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(target, '')
hostSetElementText(nextTarget, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as HostVNode[]).length; i++) {
move(
(children as HostVNode[])[i],
nextTarget,
null,
MoveType.REORDER
)
}
}
} else if (__DEV__) {
warn('Invalid Portal target on update:', target, `(${typeof target})`)
}
}
}
// insert an empty node as the placeholder for the portal
processCommentNode(n1, n2, container, anchor)
}
const processComponent = (
n1: HostVNode | null,
n2: HostVNode,
@ -1222,15 +1171,15 @@ function baseCreateRenderer<
resolveSlots(instance, nextVNode.children)
}
const patchChildren = (
n1: HostVNode | null,
n2: HostVNode,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean,
optimized: boolean = false
const patchChildren: PatchChildrenFn<HostNode, HostElement> = (
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized = false
) => {
const c1 = n1 && n1.children
const prevShapeFlag = n1 ? n1.shapeFlag : 0
@ -1899,20 +1848,25 @@ function baseCreateRenderer<
}
const internals: RendererInternals<HostNode, HostElement> = {
patch,
unmount,
move,
next: getNextHostNode,
options
p: patch,
um: unmount,
m: move,
mt: mountComponent,
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
n: getNextHostNode,
c: processCommentNode,
o: options
}
let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
if (createHydrationFns) {
;[hydrate, hydrateNode] = createHydrationFns(
mountComponent as MountComponentFn<Node, Element>,
hostPatchProp
)
;[hydrate, hydrateNode] = createHydrationFns(internals as RendererInternals<
Node,
Element
>)
}
return {

View File

@ -1,12 +0,0 @@
export const enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1,
STATEFUL_COMPONENT = 1 << 2,
TEXT_CHILDREN = 1 << 3,
ARRAY_CHILDREN = 1 << 4,
SLOTS_CHILDREN = 1 << 5,
SUSPENSE = 1 << 6,
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 7,
COMPONENT_KEPT_ALIVE = 1 << 8,
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

View File

@ -19,12 +19,16 @@ import {
import { RawSlots } from './componentSlots'
import { isReactive, Ref } from '@vue/reactivity'
import { AppContext } from './apiCreateApp'
import { SuspenseBoundary } from './components/Suspense'
import {
SuspenseImpl,
isSuspense,
SuspenseBoundary
} from './components/Suspense'
import { DirectiveBinding } from './directives'
import { SuspenseImpl } from './components/Suspense'
import { TransitionHooks } from './components/BaseTransition'
import { warn } from './warning'
import { currentScopeId } from './helpers/scopeId'
import { PortalImpl, isPortal } from './components/Portal'
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true
@ -32,12 +36,6 @@ export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
$props: VNodeProps
}
}
export const Portal = (Symbol(__DEV__ ? 'Portal' : undefined) as any) as {
__isPortal: true
new (): {
$props: VNodeProps & { target: string | object }
}
}
export const Text = Symbol(__DEV__ ? 'Text' : undefined)
export const Comment = Symbol(__DEV__ ? 'Comment' : undefined)
export const Static = Symbol(__DEV__ ? 'Static' : undefined)
@ -45,11 +43,11 @@ export const Static = Symbol(__DEV__ ? 'Static' : undefined)
export type VNodeTypes =
| string
| Component
| typeof Fragment
| typeof Portal
| typeof Text
| typeof Static
| typeof Comment
| typeof Fragment
| typeof PortalImpl
| typeof SuspenseImpl
export interface VNodeProps {
@ -239,13 +237,15 @@ export function createVNode(
// encode the vnode type information into a bitmap
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && (type as any).__isSuspense === true
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
: isPortal(type)
? ShapeFlags.PORTAL
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
const vnode: VNode = {
_isVNode: true,

View File

@ -8,7 +8,6 @@ import {
Text,
Comment,
Fragment,
Portal,
ssrUtils,
Slots,
warn,
@ -251,14 +250,13 @@ function renderVNode(
renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
push(`<!---->`)
break
case Portal:
renderPortal(vnode, parentComponent)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
renderElement(push, vnode, parentComponent)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
push(renderComponentVNode(vnode, parentComponent))
} else if (shapeFlag & ShapeFlags.PORTAL) {
renderPortal(vnode, parentComponent)
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
// TODO
} else {

View File

@ -5,8 +5,9 @@ export const enum ShapeFlags {
TEXT_CHILDREN = 1 << 3,
ARRAY_CHILDREN = 1 << 4,
SLOTS_CHILDREN = 1 << 5,
SUSPENSE = 1 << 6,
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 7,
COMPONENT_KEPT_ALIVE = 1 << 8,
PORTAL = 1 << 6,
SUSPENSE = 1 << 7,
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
COMPONENT_KEPT_ALIVE = 1 << 9,
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}