]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: switch to new implementation
authorEvan You <yyx990803@gmail.com>
Sat, 25 May 2019 15:51:20 +0000 (23:51 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 25 May 2019 15:51:20 +0000 (23:51 +0800)
24 files changed:
packages/runtime-core/src/component.ts [deleted file]
packages/runtime-core/src/componentComputed.ts [deleted file]
packages/runtime-core/src/componentInstance.ts [deleted file]
packages/runtime-core/src/componentOptions.ts [deleted file]
packages/runtime-core/src/componentProps.ts [deleted file]
packages/runtime-core/src/componentProxy.ts [deleted file]
packages/runtime-core/src/componentRenderUtils.ts [deleted file]
packages/runtime-core/src/componentState.ts [deleted file]
packages/runtime-core/src/componentWatch.ts [deleted file]
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/errorHandling.ts [deleted file]
packages/runtime-core/src/flags.ts [deleted file]
packages/runtime-core/src/h.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/optional/asyncComponent.ts [deleted file]
packages/runtime-core/src/optional/await.ts [deleted file]
packages/runtime-core/src/optional/directives.ts [deleted file]
packages/runtime-core/src/optional/eventEmitter.ts [deleted file]
packages/runtime-core/src/optional/keepAlive.ts [deleted file]
packages/runtime-core/src/optional/memoize.ts [deleted file]
packages/runtime-core/src/optional/mixins.ts [deleted file]
packages/runtime-core/src/patchFlags.ts [new file with mode: 0644]
packages/runtime-core/src/vdom.ts [deleted file]
packages/runtime-core/src/warning.ts [deleted file]

diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts
deleted file mode 100644 (file)
index 4e2aff2..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-import { EMPTY_OBJ, NOOP } from '@vue/shared'
-import { VNode, Slots, RenderNode, MountedVNode } from './vdom'
-import {
-  Data,
-  ComponentOptions,
-  ComponentClassOptions,
-  ComponentPropsOptions,
-  WatchOptions
-} from './componentOptions'
-import { setupWatcher } from './componentWatch'
-import { ReactiveEffect, DebuggerEvent, ComputedGetter } from '@vue/observer'
-import { nextTick } from '@vue/scheduler'
-import { ErrorTypes } from './errorHandling'
-import { initializeComponentInstance } from './componentInstance'
-import { EventEmitter, invokeListeners } from './optional/eventEmitter'
-import { warn } from './warning'
-import { ComponentProxy } from './componentProxy'
-
-// public component instance type
-export interface Component<P = {}, D = {}> extends PublicInstanceMethods {
-  readonly $el: any
-  readonly $vnode: MountedVNode
-  readonly $parentVNode: MountedVNode
-  readonly $data: D
-  readonly $props: Readonly<P>
-  readonly $attrs: Readonly<Data>
-  readonly $slots: Slots
-  readonly $root: Component
-  readonly $parent: Component
-  readonly $children: Component[]
-  readonly $options: ComponentOptions<P, D, this>
-  readonly $refs: Record<string | symbol, any>
-  readonly $proxy: this
-}
-
-interface PublicInstanceMethods {
-  $forceUpdate(): void
-  $nextTick(fn: () => any): Promise<void>
-  $watch(
-    keyOrFn: string | ((this: this) => any),
-    cb: (this: this, newValue: any, oldValue: any) => void,
-    options?: WatchOptions
-  ): () => void
-  $emit(name: string, ...payload: any[]): void
-}
-
-export interface APIMethods<P = {}, D = {}> {
-  data(): Partial<D>
-  render(props: Readonly<P>, slots: Slots, attrs: Data, parentVNode: VNode): any
-}
-
-export interface LifecycleMethods {
-  beforeCreate(): void
-  created(): void
-  beforeMount(): void
-  mounted(): void
-  beforeUpdate(vnode: VNode): void
-  updated(vnode: VNode): void
-  beforeUnmount(): void
-  unmounted(): void
-  errorCaptured(): (
-    err: Error,
-    type: ErrorTypes,
-    instance: ComponentInstance | null,
-    vnode: VNode
-  ) => boolean | void
-  activated(): void
-  deactivated(): void
-  renderTracked(e: DebuggerEvent): void
-  renderTriggered(e: DebuggerEvent): void
-}
-
-export interface ComponentClass extends ComponentClassOptions {
-  options?: ComponentOptions
-  new <P = {}, D = {}>(): Component<P, D>
-}
-
-export interface FunctionalComponent<P = {}> {
-  (props: P, slots: Slots, attrs: Data, parentVNode: VNode): any
-  props?: ComponentPropsOptions<P>
-  displayName?: string
-}
-
-export type ComponentType = ComponentClass | FunctionalComponent
-
-// Internal type that represents a mounted instance.
-// It extends ComponentImplementation with mounted instance properties.
-export interface ComponentInstance<P = {}, D = {}>
-  extends ComponentImplementation,
-    Partial<APIMethods<P, D>>,
-    Partial<LifecycleMethods> {
-  constructor: ComponentClass
-  render: APIMethods<P, D>['render']
-
-  $vnode: MountedVNode
-  $data: D
-  $props: P
-  $attrs: Data
-  $slots: Slots
-  $root: ComponentProxy
-  $children: ComponentProxy[]
-  $options: ComponentOptions<P, D>
-  $proxy: ComponentProxy<this>
-
-  _update: ReactiveEffect
-  _queueJob: ((fn: () => void) => void)
-}
-
-// actual implementation of the component
-class ComponentImplementation implements PublicInstanceMethods {
-  get $el(): any {
-    const el = this.$vnode && this.$vnode.el
-    return typeof el === 'function' ? (el as any)() : el
-  }
-
-  $vnode: VNode | null = null
-  $parentVNode: VNode | null = null
-  $data: Data | null = null
-  $props: Data | null = null
-  $attrs: Data | null = null
-  $slots: Slots | null = null
-  $root: ComponentProxy | null = null
-  $parent: ComponentProxy | null = null
-  $children: ComponentProxy[] = []
-  $options: ComponentOptions | null = null
-  $refs: Record<string, ComponentInstance | RenderNode> = {}
-  $proxy: ComponentProxy<this> | null = null
-
-  _rawData: Data | null = null
-  _computedGetters: Record<string, ComputedGetter> | null = null
-  _watchHandles: Set<ReactiveEffect> | null = null
-  _mounted: boolean = false
-  _unmounted: boolean = false
-  _events: { [event: string]: Function[] | null } | null = null
-  _update: ReactiveEffect | null = null
-  _queueJob: ((fn: () => void) => void) | null = null
-  _isVue: boolean = true
-  _inactiveRoot: boolean = false
-
-  constructor(props?: object) {
-    if (props === void 0) {
-      // When invoked without any arguments, this is the default path where
-      // we initiailize a proper component instance. Note the returned value
-      // here is actually a proxy of the raw instance (and will be the `this`
-      // context) in all sub-class methods, including the constructor!
-      return initializeComponentInstance(this as any) as any
-    } else {
-      // the presence of the props argument indicates that this class is being
-      // instantiated as a mixin, and should expose the props on itself
-      // so that the extended class constructor (and property initializers) can
-      // access $props.
-      this.$props = props
-      Object.assign(this, props)
-    }
-    if (__COMPAT__) {
-      ;(this as any)._eventEmitter = new EventEmitter(this)
-    }
-  }
-
-  // necessary to tell this apart from a functional
-  render(...args: any[]): any {
-    if (__DEV__) {
-      const name =
-        (this.$options && this.$options.displayName) || this.constructor.name
-      warn(`Class component \`${name}\` is missing render() method.`)
-    }
-  }
-
-  // to be set by renderer during mount
-  $forceUpdate: () => void = NOOP
-
-  $nextTick(fn: () => any): Promise<void> {
-    return nextTick(fn)
-  }
-
-  $watch(
-    keyOrFn: string | ((this: this) => any),
-    cb: (this: this, newValue: any, oldValue: any) => void,
-    options?: WatchOptions
-  ): () => void {
-    return setupWatcher(this as any, keyOrFn, cb, options)
-  }
-
-  $emit(name: string, ...payload: any[]) {
-    const parentData =
-      (this.$parentVNode && this.$parentVNode.data) || EMPTY_OBJ
-    const parentListener =
-      parentData['on' + name] || parentData['on' + name.toLowerCase()]
-    if (parentListener) {
-      invokeListeners(parentListener, payload)
-    }
-  }
-}
-
-// legacy event emitter interface exposed on component instances
-if (__COMPAT__) {
-  const p = ComponentImplementation.prototype as any
-  ;['on', 'off', 'once'].forEach(key => {
-    p['$' + key] = function(...args: any[]) {
-      this._eventEmitter[key](...args)
-      return this
-    }
-  })
-  const emit = p.$emit
-  p.$emit = function(...args: any[]) {
-    emit.call(this, ...args)
-    this._eventEmitter.emit(...args)
-    return this
-  }
-}
-
-// the exported Component has the implementation details of the actual
-// ComponentImplementation class but with proper type inference of ComponentClass.
-export const Component = ComponentImplementation as ComponentClass
diff --git a/packages/runtime-core/src/componentComputed.ts b/packages/runtime-core/src/componentComputed.ts
deleted file mode 100644 (file)
index 0cc7796..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-import { NOOP, isFunction } from '@vue/shared'
-import { computed, stop, ComputedGetter } from '@vue/observer'
-import { ComponentInstance } from './component'
-import { ComponentComputedOptions } from './componentOptions'
-
-export type ComputedHandles = Record<string, ComputedGetter>
-
-export function initializeComputed(
-  instance: ComponentInstance,
-  computedOptions: ComponentComputedOptions | undefined
-) {
-  if (!computedOptions) {
-    return
-  }
-  const handles: ComputedHandles = (instance._computedGetters = {})
-  const proxy = instance.$proxy
-  for (const key in computedOptions) {
-    const option = computedOptions[key]
-    const getter = isFunction(option) ? option : option.get || NOOP
-    handles[key] = computed(getter, proxy)
-  }
-}
-
-export function teardownComputed(instance: ComponentInstance) {
-  const handles = instance._computedGetters
-  if (handles !== null) {
-    for (const key in handles) {
-      stop(handles[key].effect)
-    }
-  }
-}
diff --git a/packages/runtime-core/src/componentInstance.ts b/packages/runtime-core/src/componentInstance.ts
deleted file mode 100644 (file)
index f0dc70f..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-import { VNode, MountedVNode } from './vdom'
-import { ComponentInstance, ComponentClass } from './component'
-import { initializeState } from './componentState'
-import { initializeProps } from './componentProps'
-import { initializeWatch, teardownWatch } from './componentWatch'
-import { initializeComputed, teardownComputed } from './componentComputed'
-import { ComponentProxy, createRenderProxy } from './componentProxy'
-import { resolveComponentOptionsFromClass } from './componentOptions'
-import { VNodeFlags } from './flags'
-import { ErrorTypes, callLifecycleHookWithHandler } from './errorHandling'
-import { stop } from '@vue/observer'
-import { EMPTY_OBJ } from '@vue/shared'
-
-let currentVNode: VNode | null = null
-let currentContextVNode: VNode | null = null
-
-export function createComponentInstance(vnode: VNode): ComponentInstance {
-  // component instance creation is done in two steps.
-  // first, `initializeComponentInstance` is called inside base component
-  // constructor as the instance is created so that the extended component's
-  // constructor has access to public properties and most importantly props.
-  // we are storing the vnodes in variables here so that there's no need to
-  // always pass args in super()
-  currentVNode = vnode
-  currentContextVNode = vnode.contextVNode
-  const Component = vnode.tag as ComponentClass
-  const instanceProxy = new Component() as ComponentProxy
-  const instance = instanceProxy._self
-
-  // then we finish the initialization by collecting properties set on the
-  // instance
-  const {
-    $options: { created, computed, watch }
-  } = instance
-  initializeState(instance, !Component.fromOptions)
-  initializeComputed(instance, computed)
-  initializeWatch(instance, watch)
-  instance.$slots = currentVNode.slots || EMPTY_OBJ
-
-  if (created) {
-    callLifecycleHookWithHandler(created, instanceProxy, ErrorTypes.CREATED)
-  }
-
-  currentVNode = currentContextVNode = null
-  return instance
-}
-
-// this is called inside the base component's constructor
-// it initializes all the way up to props so that they are available
-// inside the extended component's constructor, and returns the proxy of the
-// raw instance.
-export function initializeComponentInstance<T extends ComponentInstance>(
-  instance: T
-): ComponentProxy<T> {
-  if (__DEV__ && currentVNode === null) {
-    throw new Error(
-      `Component classes are not meant to be manually instantiated.`
-    )
-  }
-
-  instance.$options = resolveComponentOptionsFromClass(instance.constructor)
-  instance.$parentVNode = currentVNode as MountedVNode
-
-  // renderProxy
-  const proxy = (instance.$proxy = createRenderProxy(instance))
-
-  // parent chain management
-  if (currentContextVNode !== null) {
-    // locate first non-functional parent
-    while (currentContextVNode !== null) {
-      if ((currentContextVNode.flags & VNodeFlags.COMPONENT_STATEFUL) > 0) {
-        const parentComponent = (currentContextVNode as VNode)
-          .children as ComponentInstance
-        instance.$parent = parentComponent.$proxy
-        instance.$root = parentComponent.$root
-        parentComponent.$children.push(proxy)
-        break
-      }
-      currentContextVNode = currentContextVNode.contextVNode
-    }
-  } else {
-    instance.$root = proxy
-  }
-
-  // beforeCreate hook is called right in the constructor
-  const { beforeCreate, props } = instance.$options
-  if (beforeCreate) {
-    callLifecycleHookWithHandler(beforeCreate, proxy, ErrorTypes.BEFORE_CREATE)
-  }
-  initializeProps(instance, props, (currentVNode as VNode).data)
-
-  return proxy
-}
-
-export function teardownComponentInstance(instance: ComponentInstance) {
-  const parentComponent = instance.$parent && instance.$parent._self
-  if (parentComponent && !parentComponent._unmounted) {
-    parentComponent.$children.splice(
-      parentComponent.$children.indexOf(instance.$proxy),
-      1
-    )
-  }
-  stop(instance._update)
-  teardownComputed(instance)
-  teardownWatch(instance)
-}
diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts
deleted file mode 100644 (file)
index 6ce1092..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-import {
-  Component,
-  ComponentInstance,
-  ComponentClass,
-  APIMethods,
-  LifecycleMethods
-} from './component'
-import { isArray, isObject, isFunction } from '@vue/shared'
-import { normalizePropsOptions } from './componentProps'
-import { warn } from './warning'
-import { h } from './h'
-
-export type Data = Record<string, any>
-
-export interface ComponentClassOptions<P = {}, This = ComponentInstance> {
-  props?: ComponentPropsOptions<P>
-  computed?: ComponentComputedOptions<This>
-  watch?: ComponentWatchOptions<This>
-  displayName?: string
-  fromOptions?: boolean
-}
-
-export interface ComponentOptions<
-  P = {},
-  D = {},
-  This = ComponentInstance<P, D>
->
-  extends ComponentClassOptions<P, This>,
-    Partial<APIMethods<P, D>>,
-    Partial<LifecycleMethods> {
-  // TODO other options
-  readonly [key: string]: any
-}
-
-export type ComponentPropsOptions<P = Data> = {
-  [K in keyof P]: PropValidator<P[K]>
-}
-
-export type Prop<T> = { (): T } | { new (...args: any[]): T & object }
-
-export type PropType<T> = Prop<T> | Prop<T>[]
-
-export type PropValidator<T> = PropOptions<T> | PropType<T>
-
-export interface PropOptions<T = any> {
-  type?: PropType<T> | true | null
-  required?: boolean
-  default?: T | null | undefined | (() => T | null | undefined)
-  validator?(value: T): boolean
-}
-
-export interface ComponentComputedOptions<This = ComponentInstance> {
-  [key: string]: ((this: This, c: This) => any) | SingleComputedOptions<This>
-}
-
-type SingleComputedOptions<This> = {
-  get: (this: This, c: This) => any
-  set?: (value: any) => void
-  cache?: boolean
-}
-
-export interface ComponentWatchOptions<This = ComponentInstance> {
-  [key: string]: ComponentWatchOption<This>
-}
-
-export type ComponentWatchOption<This = ComponentInstance> =
-  | WatchHandler<This>
-  | WatchHandler<This>[]
-  | WatchOptionsWithHandler<This>
-  | string
-
-export type WatchHandler<This = any> = (
-  this: This,
-  val: any,
-  oldVal: any
-) => void
-
-export interface WatchOptionsWithHandler<This = any> extends WatchOptions {
-  handler: WatchHandler<This>
-}
-
-export interface WatchOptions {
-  sync?: boolean
-  deep?: boolean
-  immediate?: boolean
-}
-
-type ReservedKeys = { [K in keyof (APIMethods & LifecycleMethods)]: 1 }
-
-export const reservedMethods: ReservedKeys = {
-  data: 1,
-  render: 1,
-  beforeCreate: 1,
-  created: 1,
-  beforeMount: 1,
-  mounted: 1,
-  beforeUpdate: 1,
-  updated: 1,
-  beforeUnmount: 1,
-  unmounted: 1,
-  errorCaptured: 1,
-  activated: 1,
-  deactivated: 1,
-  renderTracked: 1,
-  renderTriggered: 1
-}
-
-export function isReservedKey(key: string): boolean {
-  return key[0] === '_' || key[0] === '$' || reservedMethods.hasOwnProperty(key)
-}
-
-// This is a special marker from the @prop decorator.
-// The decorator stores prop options on the Class' prototype as __prop_xxx
-const propPrefixRE = /^__prop_/
-
-// This is called in the base component constructor and the return value is
-// set on the instance as $options.
-export function resolveComponentOptionsFromClass(
-  Class: ComponentClass
-): ComponentOptions {
-  if (Class.hasOwnProperty('options')) {
-    return Class.options as ComponentOptions
-  }
-  let options = {} as any
-
-  const staticDescriptors = Object.getOwnPropertyDescriptors(Class)
-  for (const key in staticDescriptors) {
-    const { enumerable, get, value } = staticDescriptors[key]
-    if (enumerable || get) {
-      options[key] = get ? get() : value
-    }
-  }
-
-  // pre-normalize array props options into object.
-  // we may need to attach more props to it (declared by decorators)
-  if (Array.isArray(options.props)) {
-    options.props = normalizePropsOptions(options.props)
-  }
-
-  const instanceDescriptors = Object.getOwnPropertyDescriptors(Class.prototype)
-  for (const key in instanceDescriptors) {
-    const { get, value } = instanceDescriptors[key]
-    if (get) {
-      // computed properties
-      ;(options.computed || (options.computed = {}))[key] = get
-      // there's no need to do anything for the setter
-      // as it's already defined on the prototype
-    } else if (isFunction(value) && key !== 'constructor') {
-      if (key in reservedMethods) {
-        // lifecycle hooks / reserved methods
-        options[key] = value
-      } else {
-        // normal methods
-        ;(options.methods || (options.methods = {}))[key] = value
-      }
-    } else if (propPrefixRE.test(key)) {
-      // decorator-declared props
-      const propName = key.replace(propPrefixRE, '')
-      ;(options.props || (options.props = {}))[propName] = value
-    }
-  }
-
-  // post-normalize all prop options into same object format
-  if (options.props) {
-    options.props = normalizePropsOptions(options.props)
-  }
-
-  const ParentClass = Object.getPrototypeOf(Class)
-  if (ParentClass !== Component) {
-    const parentOptions = resolveComponentOptionsFromClass(ParentClass)
-    options = mergeComponentOptions(parentOptions, options)
-  }
-
-  Class.options = options
-  return options
-}
-
-export function createComponentClassFromOptions(
-  options: ComponentOptions
-): ComponentClass {
-  class AnonymousComponent extends Component {
-    static options = options
-    // indicate this component was created from options
-    static fromOptions = true
-  }
-  const proto = AnonymousComponent.prototype as any
-  for (const key in options) {
-    const value = options[key]
-    if (key === 'render') {
-      if (__COMPAT__) {
-        options.render = function() {
-          return value.call(this, h)
-        }
-      }
-      // so that we can call instance.render directly
-      proto.render = options.render
-    } else if (key === 'computed') {
-      // create computed setters on prototype
-      // (getters are handled by the render proxy)
-      for (const computedKey in value) {
-        const computed = value[computedKey]
-        const set = isObject(computed) && computed.set
-        if (set) {
-          Object.defineProperty(proto, computedKey, {
-            configurable: true,
-            set
-          })
-        }
-      }
-    } else if (key === 'methods') {
-      for (const method in value) {
-        if (__DEV__ && proto.hasOwnProperty(method)) {
-          warn(
-            `Object syntax contains method name that conflicts with ` +
-              `lifecycle hook: "${method}"`
-          )
-        }
-        proto[method] = value[method]
-      }
-    } else if (__COMPAT__) {
-      if (key === 'name') {
-        options.displayName = value
-      } else if (key === 'render') {
-        options.render = function() {
-          return value.call(this, h)
-        }
-      } else if (key === 'beforeDestroy') {
-        options.beforeUnmount = value
-      } else if (key === 'destroyed') {
-        options.unmounted = value
-      }
-    }
-  }
-  return AnonymousComponent as ComponentClass
-}
-
-export function mergeComponentOptions(to: any, from: any): ComponentOptions {
-  const res: any = Object.assign({}, to)
-  if (from.mixins) {
-    from.mixins.forEach((mixin: any) => {
-      from = mergeComponentOptions(from, mixin)
-    })
-  }
-  for (const key in from) {
-    const value = from[key]
-    const existing = res[key]
-    if (isFunction(value) && isFunction(existing)) {
-      if (key === 'data') {
-        // for data we need to merge the returned value
-        res[key] = mergeDataFn(existing, value)
-      } else if (/^render|^errorCaptured/.test(key)) {
-        // render, renderTracked, renderTriggered & errorCaptured
-        // are never merged
-        res[key] = value
-      } else {
-        // merge lifecycle hooks
-        res[key] = mergeLifecycleHooks(existing, value)
-      }
-    } else if (isArray(value) && isArray(existing)) {
-      res[key] = existing.concat(value)
-    } else if (isObject(value) && isObject(existing)) {
-      res[key] = Object.assign({}, existing, value)
-    } else {
-      res[key] = value
-    }
-  }
-  return res
-}
-
-export function mergeLifecycleHooks(a: Function, b: Function): Function {
-  return function(...args: any[]) {
-    a.call(this, ...args)
-    b.call(this, ...args)
-  }
-}
-
-export function mergeDataFn(a: Function, b: Function): Function {
-  // TODO: backwards compat requires recursive merge,
-  // but maybe we should just warn if we detect clashing keys
-  return function() {
-    return Object.assign(a.call(this), b.call(this))
-  }
-}
diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts
deleted file mode 100644 (file)
index ba80049..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-import { immutable, unwrap } from '@vue/observer'
-import { ComponentInstance } from './component'
-import {
-  Data,
-  PropOptions,
-  Prop,
-  PropType,
-  ComponentPropsOptions,
-  isReservedKey
-} from './componentOptions'
-import {
-  EMPTY_OBJ,
-  camelize,
-  hyphenate,
-  capitalize,
-  isString,
-  isFunction,
-  isArray,
-  isObject
-} from '@vue/shared'
-import { warn } from './warning'
-
-const enum BooleanFlags {
-  shouldCast = '1',
-  shouldCastTrue = '2'
-}
-
-type NormalizedProp = PropOptions & {
-  [BooleanFlags.shouldCast]?: boolean
-  [BooleanFlags.shouldCastTrue]?: boolean
-}
-
-type NormalizedPropsOptions = Record<string, NormalizedProp>
-
-export function initializeProps(
-  instance: ComponentInstance,
-  options: NormalizedPropsOptions | undefined,
-  data: Data | null
-) {
-  const { 0: props, 1: attrs } = resolveProps(data, options)
-  instance.$props = __DEV__ ? immutable(props) : props
-  instance.$attrs = options
-    ? __DEV__
-      ? immutable(attrs)
-      : attrs
-    : instance.$props
-}
-
-// resolve raw VNode data.
-// - filter out reserved keys (key, ref, slots)
-// - extract class and style into $attrs (to be merged onto child
-//   component root)
-// - for the rest:
-//   - if has declared props: put declared ones in `props`, the rest in `attrs`
-//   - else: everything goes in `props`.
-
-const EMPTY_PROPS = [EMPTY_OBJ, EMPTY_OBJ] as [Data, Data]
-
-export function resolveProps(
-  rawData: any,
-  _options: NormalizedPropsOptions | void
-): [Data, Data] {
-  const hasDeclaredProps = _options != null
-  const options = _options as NormalizedPropsOptions
-  if (!rawData && !hasDeclaredProps) {
-    return EMPTY_PROPS
-  }
-  const props: any = {}
-  let attrs: any = void 0
-  if (rawData != null) {
-    for (const key in rawData) {
-      // key, ref, slots are reserved
-      if (key === 'key' || key === 'ref' || key === 'slots') {
-        continue
-      }
-      // any non-declared data are put into a separate `attrs` object
-      // for spreading
-      if (hasDeclaredProps && !options.hasOwnProperty(key)) {
-        ;(attrs || (attrs = {}))[key] = rawData[key]
-      } else {
-        props[key] = rawData[key]
-      }
-    }
-  }
-  // set default values, cast booleans & run validators
-  if (hasDeclaredProps) {
-    for (const key in options) {
-      let opt = options[key]
-      if (opt == null) continue
-      const isAbsent = !props.hasOwnProperty(key)
-      const hasDefault = opt.hasOwnProperty('default')
-      const currentValue = props[key]
-      // default values
-      if (hasDefault && currentValue === undefined) {
-        const defaultValue = opt.default
-        props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
-      }
-      // boolean casting
-      if (opt[BooleanFlags.shouldCast]) {
-        if (isAbsent && !hasDefault) {
-          props[key] = false
-        } else if (
-          opt[BooleanFlags.shouldCastTrue] &&
-          (currentValue === '' || currentValue === hyphenate(key))
-        ) {
-          props[key] = true
-        }
-      }
-      // runtime validation
-      if (__DEV__ && rawData) {
-        validateProp(key, unwrap(rawData[key]), opt, isAbsent)
-      }
-    }
-  } else {
-    // if component has no declared props, $attrs === $props
-    attrs = props
-  }
-  return [props, attrs]
-}
-
-export function normalizePropsOptions(
-  raw: ComponentPropsOptions | void
-): NormalizedPropsOptions | void {
-  if (!raw) {
-    return
-  }
-  const normalized: NormalizedPropsOptions = {}
-  if (isArray(raw)) {
-    for (let i = 0; i < raw.length; i++) {
-      if (__DEV__ && !isString(raw[i])) {
-        warn(`props must be strings when using array syntax.`, raw[i])
-      }
-      const normalizedKey = camelize(raw[i])
-      if (!isReservedKey(normalizedKey)) {
-        normalized[normalizedKey] = EMPTY_OBJ
-      } else if (__DEV__) {
-        warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
-      }
-    }
-  } else {
-    if (__DEV__ && !isObject(raw)) {
-      warn(`invalid props options`, raw)
-    }
-    for (const key in raw) {
-      const normalizedKey = camelize(key)
-      if (!isReservedKey(normalizedKey)) {
-        const opt = raw[key]
-        const prop = (normalized[normalizedKey] =
-          isArray(opt) || isFunction(opt) ? { type: opt } : opt)
-        if (prop) {
-          const booleanIndex = getTypeIndex(Boolean, prop.type)
-          const stringIndex = getTypeIndex(String, prop.type)
-          ;(prop as NormalizedProp)[BooleanFlags.shouldCast] = booleanIndex > -1
-          ;(prop as NormalizedProp)[BooleanFlags.shouldCastTrue] =
-            booleanIndex < stringIndex
-        }
-      } else if (__DEV__) {
-        warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
-      }
-    }
-  }
-  return normalized
-}
-
-// use function string name to check type constructors
-// so that it works across vms / iframes.
-function getType(ctor: Prop<any>): string {
-  const match = ctor && ctor.toString().match(/^\s*function (\w+)/)
-  return match ? match[1] : ''
-}
-
-function isSameType(a: Prop<any>, b: Prop<any>): boolean {
-  return getType(a) === getType(b)
-}
-
-function getTypeIndex(
-  type: Prop<any>,
-  expectedTypes: PropType<any> | void | null | true
-): number {
-  if (isArray(expectedTypes)) {
-    for (let i = 0, len = expectedTypes.length; i < len; i++) {
-      if (isSameType(expectedTypes[i], type)) {
-        return i
-      }
-    }
-  } else if (isObject(expectedTypes)) {
-    return isSameType(expectedTypes, type) ? 0 : -1
-  }
-  return -1
-}
-
-type AssertionResult = {
-  valid: boolean
-  expectedType: string
-}
-
-function validateProp(
-  name: string,
-  value: any,
-  prop: PropOptions<any>,
-  isAbsent: boolean
-) {
-  const { type, required, validator } = prop
-  // required!
-  if (required && isAbsent) {
-    warn('Missing required prop: "' + name + '"')
-    return
-  }
-  // missing but optional
-  if (value == null && !prop.required) {
-    return
-  }
-  // type check
-  if (type != null && type !== true) {
-    let isValid = false
-    const types = isArray(type) ? type : [type]
-    const expectedTypes = []
-    // value is valid as long as one of the specified types match
-    for (let i = 0; i < types.length && !isValid; i++) {
-      const { valid, expectedType } = assertType(value, types[i])
-      expectedTypes.push(expectedType || '')
-      isValid = valid
-    }
-    if (!isValid) {
-      warn(getInvalidTypeMessage(name, value, expectedTypes))
-      return
-    }
-  }
-  // custom validator
-  if (validator && !validator(value)) {
-    warn('Invalid prop: custom validator check failed for prop "' + name + '".')
-  }
-}
-
-const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
-
-function assertType(value: any, type: Prop<any>): AssertionResult {
-  let valid
-  const expectedType = getType(type)
-  if (simpleCheckRE.test(expectedType)) {
-    const t = typeof value
-    valid = t === expectedType.toLowerCase()
-    // for primitive wrapper objects
-    if (!valid && t === 'object') {
-      valid = value instanceof type
-    }
-  } else if (expectedType === 'Object') {
-    valid = toRawType(value) === 'Object'
-  } else if (expectedType === 'Array') {
-    valid = isArray(value)
-  } else {
-    valid = value instanceof type
-  }
-  return {
-    valid,
-    expectedType
-  }
-}
-
-function getInvalidTypeMessage(
-  name: string,
-  value: any,
-  expectedTypes: string[]
-): string {
-  let message =
-    `Invalid prop: type check failed for prop "${name}".` +
-    ` Expected ${expectedTypes.map(capitalize).join(', ')}`
-  const expectedType = expectedTypes[0]
-  const receivedType = toRawType(value)
-  const expectedValue = styleValue(value, expectedType)
-  const receivedValue = styleValue(value, receivedType)
-  // check if we need to specify expected value
-  if (
-    expectedTypes.length === 1 &&
-    isExplicable(expectedType) &&
-    !isBoolean(expectedType, receivedType)
-  ) {
-    message += ` with value ${expectedValue}`
-  }
-  message += `, got ${receivedType} `
-  // check if we need to specify received value
-  if (isExplicable(receivedType)) {
-    message += `with value ${receivedValue}.`
-  }
-  return message
-}
-
-function styleValue(value: any, type: string): string {
-  if (type === 'String') {
-    return `"${value}"`
-  } else if (type === 'Number') {
-    return `${Number(value)}`
-  } else {
-    return `${value}`
-  }
-}
-
-function toRawType(value: any): string {
-  return Object.prototype.toString.call(value).slice(8, -1)
-}
-
-function isExplicable(type: string): boolean {
-  const explicitTypes = ['string', 'number', 'boolean']
-  return explicitTypes.some(elem => type.toLowerCase() === elem)
-}
-
-function isBoolean(...args: string[]): boolean {
-  return args.some(elem => elem.toLowerCase() === 'boolean')
-}
diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts
deleted file mode 100644 (file)
index b65cfe2..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-import { ComponentInstance } from './component'
-import { isFunction } from '@vue/shared'
-import { isRendering } from './componentRenderUtils'
-import { isReservedKey, reservedMethods } from './componentOptions'
-import { warn } from './warning'
-
-const bindCache = new WeakMap()
-
-// TODO: bound methods should also capture/handle errors
-function getBoundMethod(fn: Function, target: any, receiver: any): Function {
-  let boundMethodsForTarget = bindCache.get(target)
-  if (boundMethodsForTarget === void 0) {
-    bindCache.set(target, (boundMethodsForTarget = new Map()))
-  }
-  let boundFn = boundMethodsForTarget.get(fn)
-  if (boundFn === void 0) {
-    boundMethodsForTarget.set(fn, (boundFn = fn.bind(receiver)))
-  }
-  return boundFn
-}
-
-const renderProxyHandlers = {
-  get(target: ComponentInstance<any, any>, key: string, receiver: any) {
-    let i: any
-    if (key === '_self') {
-      return target
-    } else if ((i = target._rawData) !== null && i.hasOwnProperty(key)) {
-      // data
-      // make sure to return from $data to register dependency
-      return target.$data[key]
-    } else if ((i = target.$options.props) != null && i.hasOwnProperty(key)) {
-      // props are only proxied if declared
-      return target.$props[key]
-    } else if (
-      (i = target._computedGetters) !== null &&
-      i.hasOwnProperty(key)
-    ) {
-      // computed
-      return i[key]()
-    } else if (key[0] !== '_') {
-      if (__DEV__ && isRendering) {
-        if (key in reservedMethods) {
-          warn(
-            `"${key}" is a reserved method / lifecycle hook and should not be ` +
-              `used as a normal method during render.`
-          )
-        } else if (!(key in target)) {
-          warn(
-            `property "${key}" was accessed during render but does not exist ` +
-              `on instance.`
-          )
-        }
-      }
-      const value = Reflect.get(target, key, receiver)
-      if (key !== 'constructor' && isFunction(value)) {
-        // auto bind
-        return getBoundMethod(value, target, receiver)
-      } else {
-        return value
-      }
-    }
-  },
-  set(
-    target: ComponentInstance<any, any>,
-    key: string,
-    value: any,
-    receiver: any
-  ): boolean {
-    let i: any
-    if (__DEV__) {
-      if (isReservedKey(key) && key in target) {
-        warn(`failed setting property "${key}": reserved fields are immutable.`)
-        return false
-      }
-      if ((i = target.$options.props) != null && i.hasOwnProperty(key)) {
-        warn(`failed setting property "${key}": props are immutable.`)
-        return false
-      }
-    }
-    if ((i = target._rawData) !== null && i.hasOwnProperty(key)) {
-      target.$data[key] = value
-      return true
-    } else {
-      return Reflect.set(target, key, value, receiver)
-    }
-  }
-}
-
-export type ComponentProxy<T = ComponentInstance> = T & { _self: T }
-
-export function createRenderProxy<T extends ComponentInstance>(
-  instance: T
-): ComponentProxy<T> {
-  debugger
-  return new Proxy(instance, renderProxyHandlers) as any
-}
diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts
deleted file mode 100644 (file)
index d327d0f..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-import { VNode, createFragment, createTextVNode, cloneVNode } from './vdom'
-import { ComponentInstance, FunctionalComponent } from './component'
-import { resolveProps } from './componentProps'
-import { handleError, ErrorTypes } from './errorHandling'
-import { VNodeFlags, ChildrenFlags } from './flags'
-import { EMPTY_OBJ, isArray, isObject } from '@vue/shared'
-
-export let isRendering = false
-
-export function renderInstanceRoot(instance: ComponentInstance): VNode {
-  let vnode
-  const { render, $proxy, $props, $slots, $attrs, $parentVNode } = instance
-  if (__DEV__) {
-    isRendering = true
-  }
-  try {
-    vnode = render.call($proxy, $props, $slots, $attrs, $parentVNode)
-  } catch (err) {
-    if (__DEV__) {
-      isRendering = false
-    }
-    handleError(err, instance, ErrorTypes.RENDER)
-  }
-  if (__DEV__) {
-    isRendering = false
-  }
-  return normalizeComponentRoot(vnode, $parentVNode)
-}
-
-export function renderFunctionalRoot(vnode: VNode): VNode {
-  const render = vnode.tag as FunctionalComponent
-  const { 0: props, 1: attrs } = resolveProps(vnode.data, render.props)
-  let subTree
-  try {
-    subTree = render(props, vnode.slots || EMPTY_OBJ, attrs, vnode)
-  } catch (err) {
-    handleError(err, vnode, ErrorTypes.RENDER)
-  }
-  return normalizeComponentRoot(subTree, vnode)
-}
-
-function normalizeComponentRoot(
-  vnode: any,
-  componentVNode: VNode | null
-): VNode {
-  if (vnode == null) {
-    vnode = createTextVNode('')
-  } else if (!isObject(vnode)) {
-    vnode = createTextVNode(vnode + '')
-  } else if (isArray(vnode)) {
-    if (vnode.length === 1) {
-      vnode = normalizeComponentRoot(vnode[0], componentVNode)
-    } else {
-      vnode = createFragment(vnode)
-    }
-  } else {
-    const { el, flags } = vnode
-    if (
-      componentVNode &&
-      (flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
-    ) {
-      if (el) {
-        vnode = cloneVNode(vnode as VNode)
-      }
-      if (flags & VNodeFlags.COMPONENT) {
-        vnode.parentVNode = componentVNode
-      }
-    } else if (el) {
-      vnode = cloneVNode(vnode as VNode)
-    }
-  }
-  return vnode
-}
-
-export function shouldUpdateComponent(
-  prevVNode: VNode,
-  nextVNode: VNode
-): boolean {
-  const { data: prevProps, childFlags: prevChildFlags } = prevVNode
-  const { data: nextProps, childFlags: nextChildFlags } = nextVNode
-  // If has different slots content, or has non-compiled slots,
-  // the child needs to be force updated.
-  if (
-    prevChildFlags !== nextChildFlags ||
-    (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
-  ) {
-    return true
-  }
-  if (prevProps === nextProps) {
-    return false
-  }
-  if (prevProps === null) {
-    return nextProps !== null
-  }
-  if (nextProps === null) {
-    return prevProps !== null
-  }
-  const nextKeys = Object.keys(nextProps)
-  if (nextKeys.length !== Object.keys(prevProps).length) {
-    return true
-  }
-  for (let i = 0; i < nextKeys.length; i++) {
-    const key = nextKeys[i]
-    if (nextProps[key] !== prevProps[key]) {
-      return true
-    }
-  }
-  return false
-}
-
-// DEV only
-export function getReasonForComponentUpdate(
-  prevVNode: VNode,
-  nextVNode: VNode
-): any {
-  const reasons = []
-  const { childFlags: prevChildFlags } = prevVNode
-  const { childFlags: nextChildFlags } = nextVNode
-  if (
-    prevChildFlags !== nextChildFlags ||
-    (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
-  ) {
-    reasons.push({
-      type: `slots may have changed`,
-      tip: `use function slots + $stable: true to avoid slot-triggered child updates.`
-    })
-  }
-  const prevProps = prevVNode.data || EMPTY_OBJ
-  const nextProps = nextVNode.data || EMPTY_OBJ
-  for (const key in nextProps) {
-    if (nextProps[key] !== prevProps[key]) {
-      reasons.push({
-        type: 'prop changed',
-        key,
-        value: nextProps[key],
-        oldValue: prevProps[key]
-      })
-    }
-  }
-  for (const key in prevProps) {
-    if (!(key in nextProps)) {
-      reasons.push({
-        type: 'prop changed',
-        key,
-        value: undefined,
-        oldValue: prevProps[key]
-      })
-    }
-  }
-  return {
-    type: 'triggered by parent',
-    reasons
-  }
-}
diff --git a/packages/runtime-core/src/componentState.ts b/packages/runtime-core/src/componentState.ts
deleted file mode 100644 (file)
index fec671e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-import { ComponentInstance } from './component'
-import { observable } from '@vue/observer'
-import { isReservedKey } from './componentOptions'
-
-export function initializeState(
-  instance: ComponentInstance,
-  shouldExtractInitializers: boolean
-) {
-  const { data } = instance.$options
-  const rawData = (instance._rawData = (data ? data.call(instance) : {}) as any)
-  if (shouldExtractInitializers) {
-    extractInitializers(instance, rawData)
-  }
-  instance.$data = observable(rawData || {})
-}
-
-// extract properties initialized in a component's constructor
-export function extractInitializers(
-  instance: ComponentInstance,
-  data: any = {}
-): any {
-  const keys = Object.keys(instance)
-  const props = instance.$props
-  for (let i = 0; i < keys.length; i++) {
-    const key = keys[i]
-    if (!isReservedKey(key) && !props.hasOwnProperty(key)) {
-      data[key] = (instance as any)[key]
-    }
-  }
-  return data
-}
diff --git a/packages/runtime-core/src/componentWatch.ts b/packages/runtime-core/src/componentWatch.ts
deleted file mode 100644 (file)
index ad607e5..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-import {
-  EMPTY_OBJ,
-  NOOP,
-  isFunction,
-  isArray,
-  isString,
-  isObject
-} from '@vue/shared'
-import { ComponentInstance } from './component'
-import { ComponentWatchOptions, WatchOptions } from './componentOptions'
-import { effect, stop } from '@vue/observer'
-import { queueJob } from '@vue/scheduler'
-import { handleError, ErrorTypes } from './errorHandling'
-import { warn } from './warning'
-
-export function initializeWatch(
-  instance: ComponentInstance,
-  options: ComponentWatchOptions | undefined
-) {
-  if (options !== void 0) {
-    for (const key in options) {
-      const opt = options[key]
-      if (isArray(opt)) {
-        opt.forEach(o => setupWatcher(instance, key, o))
-      } else if (isFunction(opt)) {
-        setupWatcher(instance, key, opt)
-      } else if (isString(opt)) {
-        setupWatcher(instance, key, (instance as any)[opt])
-      } else if (opt.handler) {
-        setupWatcher(instance, key, opt.handler, opt)
-      }
-    }
-  }
-}
-
-export function setupWatcher(
-  instance: ComponentInstance,
-  keyOrFn: string | Function,
-  cb: (newValue: any, oldValue: any) => void,
-  options: WatchOptions = EMPTY_OBJ as WatchOptions
-): () => void {
-  const handles = instance._watchHandles || (instance._watchHandles = new Set())
-  const proxy = instance.$proxy
-
-  const rawGetter = isString(keyOrFn)
-    ? parseDotPath(keyOrFn, proxy)
-    : () => keyOrFn.call(proxy)
-
-  if (__DEV__ && rawGetter === NOOP) {
-    warn(
-      `Failed watching expression: "${keyOrFn}". ` +
-        `Watch expressions can only be dot-delimited paths. ` +
-        `For more complex expressions, use $watch with a function instead.`
-    )
-  }
-
-  const getter = options.deep ? () => traverse(rawGetter()) : rawGetter
-
-  let oldValue: any
-
-  const applyCb = () => {
-    const newValue = runner()
-    if (options.deep || newValue !== oldValue) {
-      try {
-        cb.call(instance.$proxy, newValue, oldValue)
-      } catch (e) {
-        handleError(e, instance, ErrorTypes.WATCH_CALLBACK)
-      }
-      oldValue = newValue
-    }
-  }
-
-  const runner = effect(getter, {
-    lazy: true,
-    scheduler: options.sync
-      ? applyCb
-      : () => {
-          // defer watch callback using the scheduler so that multiple mutations
-          // result in one call only.
-          queueJob(applyCb)
-        }
-  })
-
-  oldValue = runner()
-  handles.add(runner)
-
-  if (options.immediate) {
-    cb.call(instance.$proxy, oldValue, undefined)
-  }
-
-  return () => {
-    stop(runner)
-    handles.delete(runner)
-  }
-}
-
-export function teardownWatch(instance: ComponentInstance) {
-  if (instance._watchHandles !== null) {
-    instance._watchHandles.forEach(stop)
-  }
-}
-
-const bailRE = /[^\w.$]/
-
-function parseDotPath(path: string, ctx: any): Function {
-  if (bailRE.test(path)) {
-    return NOOP
-  }
-  const segments = path.split('.')
-  if (segments.length === 1) {
-    return () => ctx[path]
-  } else {
-    return () => {
-      let obj = ctx
-      for (let i = 0; i < segments.length; i++) {
-        if (!obj) return
-        obj = obj[segments[i]]
-      }
-      return obj
-    }
-  }
-}
-
-function traverse(value: any, seen: Set<any> = new Set()) {
-  if (!isObject(value) || seen.has(value)) {
-    return
-  }
-  seen.add(value)
-  if (isArray(value)) {
-    for (let i = 0; i < value.length; i++) {
-      traverse(value[i], seen)
-    }
-  } else if (value instanceof Map || value instanceof Set) {
-    ;(value as any).forEach((v: any) => {
-      traverse(v, seen)
-    })
-  } else {
-    for (const key in value) {
-      traverse(value[key], seen)
-    }
-  }
-  return value
-}
index cd7927d6d5bcf32f15f6560d96dc36568f1d6727..e09e8ce35ae75a849f5ebd945a71a080bdc92f37 100644 (file)
-import {
-  effect as createReactiveEffect,
-  stop as stopReactiveEffect,
-  ReactiveEffect,
-  immutable,
-  ReactiveEffectOptions
-} from '@vue/observer'
-import {
-  queueJob,
-  handleSchedulerError,
-  nextTick,
-  queuePostEffect,
-  flushEffects,
-  queueNodeOp
-} from '@vue/scheduler'
-import { VNodeFlags, ChildrenFlags } from './flags'
-import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
-import {
-  VNode,
-  MountedVNode,
-  RenderNode,
-  createTextVNode,
-  cloneVNode,
-  VNodeChildren
-} from './vdom'
-import { ComponentInstance } from './component'
-import {
-  createComponentInstance,
-  teardownComponentInstance
-} from './componentInstance'
-import {
-  renderInstanceRoot,
-  renderFunctionalRoot,
-  shouldUpdateComponent,
-  getReasonForComponentUpdate
-} from './componentRenderUtils'
-import { KeepAliveSymbol } from './optional/keepAlive'
-import { pushWarningContext, popWarningContext, warn } from './warning'
-import { resolveProps } from './componentProps'
-import {
-  handleError,
-  ErrorTypes,
-  callLifecycleHookWithHandler
-} from './errorHandling'
+// TODO:
+// - app context
+// - component
+// - lifecycle
+// - refs
+// - reused nodes
+// - hydration
 
-export interface NodeOps {
-  createElement: (tag: string, isSVG?: boolean) => any
-  createText: (text: string) => any
-  setText: (node: any, text: string) => void
-  appendChild: (parent: any, child: any) => void
-  insertBefore: (parent: any, child: any, ref: any) => void
-  removeChild: (parent: any, child: any) => void
-  clearContent: (node: any) => void
-  parentNode: (node: any) => any
-  nextSibling: (node: any) => any
-  querySelector: (selector: string) => any
-}
-
-export interface PatchDataFunction {
-  (
-    el: any,
-    key: string,
-    prevValue: any,
-    nextValue: any,
-    preVNode: VNode | null,
-    nextVNode: VNode,
-    isSVG: boolean,
-    // passed for DOM operations that removes child content
-    // e.g. innerHTML & textContent
-    unmountChildren: (children: VNode[], childFlags: ChildrenFlags) => void
-  ): void
-}
+import { Text, Fragment, Empty, createVNode } from './h.js'
 
-export interface RendererOptions {
-  nodeOps: NodeOps
-  patchData: PatchDataFunction
-}
+import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
 
-export interface FunctionalHandle {
-  prev: VNode
-  next: VNode
-  update: ReactiveEffect
-  container: RenderNode | null
-}
+const emptyArr: any[] = []
+const emptyObj = {}
 
-handleSchedulerError(err => handleError(err, null, ErrorTypes.SCHEDULER))
+const isSameType = (n1, n2) => n1.type === n2.type && n1.key === n2.key
 
-// The whole mounting / patching / unmouting logic is placed inside this
-// single function so that we can create multiple renderes with different
-// platform definitions. This allows for use cases like creating a test
-// renderer alongside an actual renderer.
-export function createRenderer(options: RendererOptions) {
+export function createRenderer(hostConfig) {
   const {
-    nodeOps: {
-      createElement: platformCreateElement,
-      createText: platformCreateText,
-      setText: platformSetText,
-      appendChild: platformAppendChild,
-      insertBefore: platformInsertBefore,
-      removeChild: platformRemoveChild,
-      clearContent: platformClearContent,
-      parentNode: platformParentNode,
-      nextSibling: platformNextSibling,
-      querySelector: platformQuerySelector
-    },
-    patchData: platformPatchData
-  } = options
-
-  function queueInsertOrAppend(
-    container: RenderNode,
-    newNode: RenderNode,
-    refNode: RenderNode | null
-  ) {
-    if (refNode === null) {
-      queueNodeOp([platformAppendChild, container, newNode])
+    insert,
+    remove,
+    patchProp: hostPatchProp,
+    createElement: hostCreateElement,
+    createText: hostCreateText,
+    createComment: hostCreateComment,
+    setText: hostSetText,
+    setElementText: hostSetElementText,
+    nextSibling: hostNextSibling
+  } = hostConfig
+
+  function patch(n1, n2, container, anchor, optimized) {
+    // patching & not same type, unmount old tree
+    if (n1 != null && !isSameType(n1, n2)) {
+      anchor = hostNextSibling(n1.el)
+      unmount(n1, true)
+      n1 = null
+    }
+
+    const { type } = n2
+    if (type === Text) {
+      processText(n1, n2, container, anchor)
+    } else if (type === Empty) {
+      processEmptyNode(n1, n2, container, anchor)
+    } else if (type === Fragment) {
+      processFragment(n1, n2, container, anchor, optimized)
+    } else if (typeof type === 'function') {
+      // TODO Component
     } else {
-      queueNodeOp([platformInsertBefore, container, newNode, refNode])
-    }
-  }
-
-  // mounting ------------------------------------------------------------------
-
-  function mount(
-    vnode: VNode,
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null,
-    ownerArray?: VNode[],
-    index?: number
-  ) {
-    const { flags } = vnode
-    if (flags & VNodeFlags.ELEMENT) {
-      mountElement(vnode, container, contextVNode, isSVG, endNode)
-    } else if (flags & VNodeFlags.COMPONENT) {
-      mountComponent(vnode, container, contextVNode, isSVG, endNode)
-    } else if (flags & VNodeFlags.TEXT) {
-      mountText(vnode, container, endNode)
-    } else if (flags & VNodeFlags.FRAGMENT) {
-      mountFragment(vnode, container, contextVNode, isSVG, endNode)
-    } else if (flags & VNodeFlags.PORTAL) {
-      mountPortal(vnode, container, contextVNode)
-    }
-  }
-
-  function mountArrayChildren(
-    children: VNode[],
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    for (let i = 0; i < children.length; i++) {
-      const child = getNextVNode(children, i)
-      mount(child, container, contextVNode, isSVG, endNode)
+      processElement(n1, n2, container, anchor, optimized)
     }
   }
 
-  function mountElement(
-    vnode: VNode,
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    const { flags, tag, data, children, childFlags, ref } = vnode
-    isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
-    // element creation is not deferred since it doesn't produce
-    // user-affecting side effects until inserted into the DOM
-    const el = (vnode.el = platformCreateElement(tag as string, isSVG))
-    if (data != null) {
-      for (const key in data) {
-        if (!reservedPropRE.test(key)) {
-          platformPatchData(
-            el,
-            key,
-            null,
-            data[key],
-            null,
-            vnode,
-            isSVG,
-            unmountChildren
-          )
-        }
-      }
-      if (data.vnodeBeforeMount) {
-        data.vnodeBeforeMount(vnode)
-      }
-    }
-    if (childFlags !== ChildrenFlags.NO_CHILDREN) {
-      const hasSVGChildren = isSVG && tag !== 'foreignObject'
-      if (childFlags & ChildrenFlags.SINGLE_VNODE) {
-        mount(children as VNode, el, contextVNode, hasSVGChildren, null)
-      } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
-        mountArrayChildren(
-          children as VNode[],
-          el,
-          contextVNode,
-          hasSVGChildren,
-          null
-        )
-      }
-    }
-    if (container != null) {
-      queueInsertOrAppend(container, el, endNode)
-    }
-    if (ref) {
-      queuePostEffect(() => {
-        ref(el)
-      })
-    }
-    if (data != null && data.vnodeMounted) {
-      queuePostEffect(() => {
-        data.vnodeMounted(vnode)
-      })
-    }
-  }
-
-  function mountComponent(
-    vnode: VNode,
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    vnode.contextVNode = contextVNode
-    const { flags } = vnode
-    if (flags & VNodeFlags.COMPONENT_STATEFUL) {
-      mountStatefulComponent(vnode, container, isSVG, endNode)
+  function processText(n1, n2, container, anchor) {
+    if (n1 == null) {
+      insert((n2.el = hostCreateText(n2.children)), container, anchor)
     } else {
-      mountFunctionalComponent(vnode, container, isSVG, endNode)
-    }
-  }
-
-  function mountStatefulComponent(
-    vnode: VNode,
-    container: RenderNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    if (vnode.flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
-      // kept-alive
-      activateComponentInstance(vnode, container, endNode)
-    } else {
-      if (__COMPAT__) {
-        mountComponentInstance(vnode, container, isSVG, endNode)
-      } else {
-        queueJob(() => mountComponentInstance(vnode, container, isSVG, endNode))
+      const el = (n2.el = n1.el)
+      if (n2.children !== n1.children) {
+        hostSetText(el, n2, children)
       }
     }
   }
 
-  function mountFunctionalComponent(
-    vnode: VNode,
-    container: RenderNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    if (__DEV__ && vnode.ref) {
-      warn(
-        `cannot use ref on a functional component because there is no ` +
-          `instance to reference to.`
-      )
-    }
-
-    const handle: FunctionalHandle = (vnode.handle = {
-      prev: vnode,
-      next: null as any,
-      update: null as any,
-      container
-    })
-
-    const doMount = () => {
-      handle.update = createReactiveEffect(
-        () => {
-          if (!handle.next) {
-            // initial mount
-            if (__DEV__) {
-              pushWarningContext(vnode)
-            }
-            const subTree = (vnode.children = renderFunctionalRoot(vnode))
-            queuePostEffect(() => {
-              vnode.el = subTree.el as RenderNode
-            })
-            mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
-            handle.next = vnode
-            if (__DEV__) {
-              popWarningContext()
-            }
-          } else {
-            updateFunctionalComponent(handle, isSVG)
-          }
-        },
-        {
-          scheduler: queueJob
-        }
-      )
-    }
-
-    // we are using vnode.ref to store the functional component's update job
-    if (__COMPAT__) {
-      doMount()
+  function processEmptyNode(n1, n2, container, anchor) {
+    if (n1 == null) {
+      insert((n2.el = hostCreateComment('')), container, anchor)
     } else {
-      queueJob(() => {
-        doMount()
-        // cleanup if mount is invalidated before committed
-        return () => {
-          stopReactiveEffect(handle.update)
-        }
-      })
-    }
-  }
-
-  function updateFunctionalComponent(handle: FunctionalHandle, isSVG: boolean) {
-    const { prev, next } = handle
-    if (__DEV__) {
-      pushWarningContext(next)
-    }
-    const prevTree = prev.children as MountedVNode
-    const nextTree = (next.children = renderFunctionalRoot(next))
-    queuePostEffect(() => {
-      next.el = nextTree.el
-    })
-    patch(
-      prevTree,
-      nextTree,
-      handle.container as RenderNode,
-      next as MountedVNode,
-      isSVG
-    )
-    if (__DEV__) {
-      popWarningContext()
-    }
-  }
-
-  function mountText(
-    vnode: VNode,
-    container: RenderNode | null,
-    endNode: RenderNode | null
-  ) {
-    const el = (vnode.el = platformCreateText(vnode.children as string))
-    if (container != null) {
-      queueInsertOrAppend(container, el, endNode)
-    }
-  }
-
-  function mountFragment(
-    vnode: VNode,
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    const { children, childFlags } = vnode
-    switch (childFlags) {
-      case ChildrenFlags.SINGLE_VNODE:
-        queuePostEffect(() => {
-          vnode.el = (children as MountedVNode).el
-        })
-        mount(children as VNode, container, contextVNode, isSVG, endNode)
-        break
-      case ChildrenFlags.NO_CHILDREN:
-        const placeholder = createTextVNode('')
-        mountText(placeholder, container, null)
-        vnode.el = placeholder.el
-        break
-      default:
-        queuePostEffect(() => {
-          vnode.el = (children as MountedVNode[])[0].el
-        })
-        mountArrayChildren(
-          children as VNode[],
-          container,
-          contextVNode,
-          isSVG,
-          endNode
-        )
-    }
-  }
-
-  function mountPortal(
-    vnode: VNode,
-    container: RenderNode | null,
-    contextVNode: MountedVNode | null
-  ) {
-    const { tag, children, childFlags, ref } = vnode
-    const target = isString(tag) ? platformQuerySelector(tag) : tag
-
-    if (__DEV__ && !target) {
-      // TODO warn poartal target not found
-    }
-
-    if (childFlags & ChildrenFlags.SINGLE_VNODE) {
-      mount(children as VNode, target as RenderNode, contextVNode, false, null)
-    } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
-      mountArrayChildren(
-        children as VNode[],
-        target as RenderNode,
-        contextVNode,
-        false,
-        null
-      )
-    }
-    if (ref) {
-      queuePostEffect(() => {
-        ref(target)
-      })
-    }
-    const placeholder = createTextVNode('')
-    mountText(placeholder, container, null)
-    vnode.el = placeholder.el
-  }
-
-  // patching ------------------------------------------------------------------
-
-  function queuePatchData(
-    el: RenderNode | (() => RenderNode),
-    key: string,
-    prevValue: any,
-    nextValue: any,
-    preVNode: VNode | null,
-    nextVNode: VNode,
-    isSVG: boolean
-  ) {
-    if (!reservedPropRE.test(key)) {
-      queueNodeOp([
-        platformPatchData,
-        el,
-        key,
-        prevValue,
-        nextValue,
-        preVNode,
-        nextVNode,
-        isSVG,
-        unmountChildren
-      ])
-    }
-  }
-
-  function patch(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean
-  ) {
-    const nextFlags = nextVNode.flags
-    const prevFlags = prevVNode.flags
-
-    if (prevFlags !== nextFlags) {
-      replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
-    } else if (nextFlags & VNodeFlags.ELEMENT) {
-      patchElement(prevVNode, nextVNode, container, contextVNode, isSVG)
-    } else if (nextFlags & VNodeFlags.COMPONENT) {
-      patchComponent(prevVNode, nextVNode, container, contextVNode, isSVG)
-    } else if (nextFlags & VNodeFlags.TEXT) {
-      patchText(prevVNode, nextVNode)
-    } else if (nextFlags & VNodeFlags.FRAGMENT) {
-      patchFragment(prevVNode, nextVNode, container, contextVNode, isSVG)
-    } else if (nextFlags & VNodeFlags.PORTAL) {
-      patchPortal(prevVNode, nextVNode, contextVNode)
+      n2.el = n1.el
     }
   }
 
-  function patchElement(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean
-  ) {
-    const { flags, tag, clonedFrom } = nextVNode
-
-    // cloned vnodes pointing to the same original.
-    // these are hoisted static trees so just skip entirely
-    if (
-      clonedFrom !== null &&
-      (clonedFrom === prevVNode || clonedFrom === prevVNode.clonedFrom)
-    ) {
-      nextVNode.el = prevVNode.el
-      return
-    }
-
-    isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
-
-    if (prevVNode.tag !== tag) {
-      replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
-      return
-    }
-
-    const el = (nextVNode.el = prevVNode.el)
-    const prevData = prevVNode.data
-    const nextData = nextVNode.data
-
-    if (nextData != null && nextData.vnodeBeforeUpdate) {
-      nextData.vnodeBeforeUpdate(nextVNode, prevVNode)
-    }
-
-    // patch data
-    if (prevData !== nextData) {
-      const prevDataOrEmpty = prevData || EMPTY_OBJ
-      const nextDataOrEmpty = nextData || EMPTY_OBJ
-      if (nextDataOrEmpty !== EMPTY_OBJ) {
-        for (const key in nextDataOrEmpty) {
-          const prevValue = prevDataOrEmpty[key]
-          const nextValue = nextDataOrEmpty[key]
-          if (prevValue !== nextValue) {
-            queuePatchData(
-              el,
-              key,
-              prevValue,
-              nextValue,
-              prevVNode,
-              nextVNode,
-              isSVG
-            )
-          }
-        }
-      }
-      if (prevDataOrEmpty !== EMPTY_OBJ) {
-        for (const key in prevDataOrEmpty) {
-          const prevValue = prevDataOrEmpty[key]
-          if (prevValue != null && !nextDataOrEmpty.hasOwnProperty(key)) {
-            queuePatchData(
-              el,
-              key,
-              prevValue,
-              null,
-              prevVNode,
-              nextVNode,
-              isSVG
-            )
-          }
-        }
-      }
-    }
-
-    // children
-    patchChildren(
-      prevVNode.childFlags,
-      nextVNode.childFlags,
-      prevVNode.children,
-      nextVNode.children,
-      el,
-      contextVNode,
-      isSVG && nextVNode.tag !== 'foreignObject',
-      null
-    )
-
-    if (nextData != null && nextData.vnodeUpdated) {
-      // TODO fix me
-      // vnodeUpdatedHooks.push(() => {
-      //   nextData.vnodeUpdated(nextVNode, prevVNode)
-      // })
-    }
-  }
-
-  function patchComponent(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean
-  ) {
-    nextVNode.contextVNode = contextVNode
-    const { tag, flags } = nextVNode
-    if (tag !== prevVNode.tag) {
-      replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
-    } else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
-      patchStatefulComponent(prevVNode, nextVNode)
+  function processElement(n1, n2, container, anchor, optimized) {
+    // mount
+    if (n1 == null) {
+      mountElement(n2, container, anchor)
     } else {
-      patchFunctionalComponent(prevVNode, nextVNode, container)
+      patchElement(n1, n2, container, optimized)
     }
   }
 
-  function patchStatefulComponent(prevVNode: MountedVNode, nextVNode: VNode) {
-    const { data: prevData } = prevVNode
-    const { data: nextData, slots: nextSlots } = nextVNode
-
-    const instance = (nextVNode.children =
-      prevVNode.children) as ComponentInstance
-
-    if (nextData !== prevData) {
-      const { 0: props, 1: attrs } = resolveProps(
-        nextData,
-        instance.$options.props
-      )
-      instance.$props = __DEV__ ? immutable(props) : props
-      instance.$attrs = __DEV__ ? immutable(attrs) : attrs
-    }
-    instance.$slots = nextSlots || EMPTY_OBJ
-    instance.$parentVNode = nextVNode as MountedVNode
-
-    if (shouldUpdateComponent(prevVNode, nextVNode)) {
-      if (__DEV__ && instance.$options.renderTriggered) {
-        callLifecycleHookWithHandler(
-          instance.$options.renderTriggered,
-          instance.$proxy,
-          ErrorTypes.RENDER_TRIGGERED,
-          getReasonForComponentUpdate(prevVNode, nextVNode)
-        )
+  function mountElement(vnode, container, anchor) {
+    const el = (vnode.el = hostCreateElement(vnode.type))
+    if (vnode.props != null) {
+      for (const key in vnode.props) {
+        hostPatchProp(el, key, vnode.props[key], null)
       }
-      instance.$forceUpdate()
-    } else if (instance.$vnode.flags & VNodeFlags.COMPONENT) {
-      instance.$vnode.contextVNode = nextVNode
     }
-    nextVNode.el = instance.$vnode.el
-  }
-
-  function patchFunctionalComponent(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode
-  ) {
-    const prevTree = prevVNode.children as VNode
-    const handle = (nextVNode.handle = prevVNode.handle as FunctionalHandle)
-    handle.prev = prevVNode
-    handle.next = nextVNode
-    handle.container = container
-
-    if (shouldUpdateComponent(prevVNode, nextVNode)) {
-      queueJob(handle.update)
-    } else if (prevTree.flags & VNodeFlags.COMPONENT) {
-      // functional component returned another component
-      prevTree.contextVNode = nextVNode
-    }
-  }
-
-  function patchFragment(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean
-  ) {
-    // determine the tail node of the previous fragment,
-    // then retrieve its next sibling to use as the end node for patchChildren.
-    const endNode = platformNextSibling(getVNodeLastEl(prevVNode))
-    const { childFlags, children } = nextVNode
-    queuePostEffect(() => {
-      switch (childFlags) {
-        case ChildrenFlags.SINGLE_VNODE:
-          nextVNode.el = (children as MountedVNode).el
-          break
-        case ChildrenFlags.NO_CHILDREN:
-          nextVNode.el = prevVNode.el
-          break
-        default:
-          nextVNode.el = (children as MountedVNode[])[0].el
-      }
-    })
-    patchChildren(
-      prevVNode.childFlags,
-      childFlags,
-      prevVNode.children,
-      children,
-      container,
-      contextVNode,
-      isSVG,
-      endNode
-    )
-  }
-
-  function getVNodeLastEl(vnode: MountedVNode): RenderNode {
-    const { el, flags, children, childFlags } = vnode
-    if (flags & VNodeFlags.FRAGMENT) {
-      if (childFlags & ChildrenFlags.SINGLE_VNODE) {
-        return getVNodeLastEl(children as MountedVNode)
-      } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
-        return getVNodeLastEl(
-          (children as MountedVNode[])[(children as MountedVNode[]).length - 1]
-        )
-      } else {
-        return el
-      }
+    if (typeof vnode.children === 'string') {
+      hostSetElementText(el, vnode.children)
     } else {
-      return el
-    }
-  }
-
-  function patchText(prevVNode: MountedVNode, nextVNode: VNode) {
-    const el = (nextVNode.el = prevVNode.el) as RenderNode
-    const nextText = nextVNode.children
-    if (nextText !== prevVNode.children) {
-      queueNodeOp([platformSetText, el, nextText])
+      mountChildren(vnode.children, el)
     }
+    insert(el, container, anchor)
   }
 
-  function patchPortal(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    contextVNode: MountedVNode | null
-  ) {
-    const prevContainer = prevVNode.tag as RenderNode
-    const nextContainer = nextVNode.tag as RenderNode
-    const nextChildren = nextVNode.children
-    patchChildren(
-      prevVNode.childFlags,
-      nextVNode.childFlags,
-      prevVNode.children,
-      nextChildren,
-      prevContainer,
-      contextVNode,
-      false,
-      null
-    )
-    nextVNode.el = prevVNode.el
-    if (nextContainer !== prevContainer) {
-      switch (nextVNode.childFlags) {
-        case ChildrenFlags.SINGLE_VNODE:
-          insertVNode(nextChildren as MountedVNode, nextContainer, null)
-          break
-        case ChildrenFlags.NO_CHILDREN:
-          break
-        default:
-          for (let i = 0; i < (nextChildren as MountedVNode[]).length; i++) {
-            insertVNode(
-              (nextChildren as MountedVNode[])[i],
-              nextContainer,
-              null
-            )
-          }
-          break
-      }
+  function mountChildren(children, container, anchor, start = 0) {
+    for (let i = start; i < children.length; i++) {
+      const child = (children[i] = normalizeChild(children[i]))
+      patch(null, child, container, anchor)
     }
   }
 
-  function replaceVNode(
-    prevVNode: MountedVNode,
-    nextVNode: VNode,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean
-  ) {
-    const refNode = platformNextSibling(getVNodeLastEl(prevVNode))
-    queueRemoveVNode(prevVNode, container)
-    mount(nextVNode, container, contextVNode, isSVG, refNode)
-  }
-
-  function patchChildren(
-    prevChildFlags: ChildrenFlags,
-    nextChildFlags: ChildrenFlags,
-    prevChildren: VNodeChildren,
-    nextChildren: VNodeChildren,
-    container: RenderNode,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    switch (prevChildFlags) {
-      case ChildrenFlags.SINGLE_VNODE:
-        switch (nextChildFlags) {
-          case ChildrenFlags.SINGLE_VNODE:
-            patch(
-              prevChildren as MountedVNode,
-              nextChildren as VNode,
-              container,
-              contextVNode,
-              isSVG
-            )
-            break
-          case ChildrenFlags.NO_CHILDREN:
-            queueRemoveVNode(prevChildren as MountedVNode, container)
-            break
-          default:
-            queueRemoveVNode(prevChildren as MountedVNode, container)
-            mountArrayChildren(
-              nextChildren as VNode[],
-              container,
-              contextVNode,
-              isSVG,
-              endNode
-            )
-            break
-        }
-        break
-      case ChildrenFlags.NO_CHILDREN:
-        switch (nextChildFlags) {
-          case ChildrenFlags.SINGLE_VNODE:
-            mount(
-              nextChildren as VNode,
-              container,
-              contextVNode,
-              isSVG,
-              endNode
-            )
-            break
-          case ChildrenFlags.NO_CHILDREN:
-            break
-          default:
-            mountArrayChildren(
-              nextChildren as VNode[],
-              container,
-              contextVNode,
-              isSVG,
-              endNode
-            )
-            break
-        }
-        break
-      default:
-        // MULTIPLE_CHILDREN
-        if (nextChildFlags === ChildrenFlags.SINGLE_VNODE) {
-          queueRemoveChildren(
-            prevChildren as MountedVNode[],
-            container,
-            endNode
-          )
-          mount(nextChildren as VNode, container, contextVNode, isSVG, endNode)
-        } else if (nextChildFlags === ChildrenFlags.NO_CHILDREN) {
-          queueRemoveChildren(
-            prevChildren as MountedVNode[],
-            container,
-            endNode
-          )
-        } else {
-          const prevLength = (prevChildren as VNode[]).length
-          const nextLength = (nextChildren as VNode[]).length
-          if (prevLength === 0) {
-            if (nextLength > 0) {
-              mountArrayChildren(
-                nextChildren as VNode[],
-                container,
-                contextVNode,
-                isSVG,
-                endNode
-              )
-            }
-          } else if (nextLength === 0) {
-            queueRemoveChildren(
-              prevChildren as MountedVNode[],
-              container,
-              endNode
-            )
-          } else if (
-            prevChildFlags === ChildrenFlags.KEYED_VNODES &&
-            nextChildFlags === ChildrenFlags.KEYED_VNODES
-          ) {
-            patchKeyedChildren(
-              prevChildren as MountedVNode[],
-              nextChildren as VNode[],
-              container,
-              prevLength,
-              nextLength,
-              contextVNode,
-              isSVG,
-              endNode
-            )
-          } else {
-            patchNonKeyedChildren(
-              prevChildren as MountedVNode[],
-              nextChildren as VNode[],
-              container,
-              prevLength,
-              nextLength,
-              contextVNode,
-              isSVG,
-              endNode
-            )
-          }
-        }
-        break
-    }
-  }
-
-  function patchNonKeyedChildren(
-    prevChildren: MountedVNode[],
-    nextChildren: VNode[],
-    container: RenderNode,
-    prevLength: number,
-    nextLength: number,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    const commonLength = prevLength > nextLength ? nextLength : prevLength
-    let i = 0
-    let nextChild
-    let prevChild
-    for (i; i < commonLength; i++) {
-      nextChild = getNextVNode(nextChildren, i)
-      prevChild = prevChildren[i]
-      patch(prevChild, nextChild, container, contextVNode, isSVG)
-      prevChildren[i] = nextChild as MountedVNode
-    }
-    if (prevLength < nextLength) {
-      for (i = commonLength; i < nextLength; i++) {
-        nextChild = getNextVNode(nextChildren, i)
-        mount(nextChild, container, contextVNode, isSVG, endNode)
-      }
-    } else if (prevLength > nextLength) {
-      for (i = commonLength; i < prevLength; i++) {
-        queueRemoveVNode(prevChildren[i], container)
-      }
+  function normalizeChild(child) {
+    // empty placeholder
+    if (child == null) {
+      return createVNode(Empty)
+    } else if (typeof child === 'string' || typeof child === 'number') {
+      return createVNode(Text, null, child + '')
+    } else if (Array.isArray(child)) {
+      return createVNode(Fragment, null, child)
+    } else {
+      return child
     }
   }
 
-  function patchKeyedChildren(
-    prevChildren: MountedVNode[],
-    nextChildren: VNode[],
-    container: RenderNode,
-    prevLength: number,
-    nextLength: number,
-    contextVNode: MountedVNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ) {
-    let prevEnd = prevLength - 1
-    let nextEnd = nextLength - 1
-    let i
-    let j = 0
-    let prevVNode = prevChildren[j]
-    let nextVNode = getNextVNode(nextChildren, j)
-    let nextPos
+  function patchElement(n1, n2, container, optimized) {
+    const el = (n2.el = n1.el)
+    const { patchFlag, dynamicChildren } = n2
+    const oldProps = (n1 && n1.props) || emptyObj
+    const newProps = n2.props || emptyObj
 
-    outer: {
-      // Sync nodes with the same key at the beginning.
-      while (prevVNode.key === nextVNode.key) {
-        patch(prevVNode, nextVNode, container, contextVNode, isSVG)
-        prevChildren[j] = nextVNode as MountedVNode
-        j++
-        if (j > prevEnd || j > nextEnd) {
-          break outer
-        }
-        prevVNode = prevChildren[j]
-        nextVNode = getNextVNode(nextChildren, j)
-      }
-
-      prevVNode = prevChildren[prevEnd]
-      nextVNode = getNextVNode(nextChildren, nextEnd)
-
-      // Sync nodes with the same key at the end.
-      while (prevVNode.key === nextVNode.key) {
-        patch(prevVNode, nextVNode, container, contextVNode, isSVG)
-        prevChildren[prevEnd] = nextVNode as MountedVNode
-        prevEnd--
-        nextEnd--
-        if (j > prevEnd || j > nextEnd) {
-          break outer
-        }
-        prevVNode = prevChildren[prevEnd]
-        nextVNode = getNextVNode(nextChildren, nextEnd)
-      }
-    }
+    if (patchFlag != null) {
+      // the presence of a patchFlag means this element's render code was
+      // generated by the compiler and can take the fast path.
+      // in this path old node and new node are guaranteed to have the same shape
+      // (i.e. at the exact same position in the source template)
 
-    if (j > prevEnd) {
-      if (j <= nextEnd) {
-        nextPos = nextEnd + 1
-        const nextNode =
-          nextPos < nextLength ? nextChildren[nextPos].el : endNode
-        while (j <= nextEnd) {
-          nextVNode = getNextVNode(nextChildren, j)
-          j++
-          mount(nextVNode, container, contextVNode, isSVG, nextNode)
+      // class
+      // this flag is matched when the element has dynamic class bindings.
+      if (patchFlag & CLASS) {
+        // TODO handle full class API, potentially optimize at compilation stage?
+        if (oldProps.class !== newProps.class) {
+          el.className = newProps.class
         }
       }
-    } else if (j > nextEnd) {
-      while (j <= prevEnd) {
-        queueRemoveVNode(prevChildren[j++], container)
-      }
-    } else {
-      let prevStart = j
-      const nextStart = j
-      const prevLeft = prevEnd - j + 1
-      const nextLeft = nextEnd - j + 1
-      const sources: number[] = []
-      for (i = 0; i < nextLeft; i++) {
-        sources.push(0)
-      }
-      // Keep track if its possible to remove whole DOM using textContent = ''
-      let canRemoveWholeContent = prevLeft === prevLength
-      let moved = false
-      let pos = 0
-      let patched = 0
-
-      // When sizes are small, just loop them through
-      if (nextLength < 4 || (prevLeft | nextLeft) < 32) {
-        for (i = prevStart; i <= prevEnd; i++) {
-          prevVNode = prevChildren[i]
-          if (patched < nextLeft) {
-            for (j = nextStart; j <= nextEnd; j++) {
-              nextVNode = getNextVNode(nextChildren, j)
-              if (prevVNode.key === nextVNode.key) {
-                sources[j - nextStart] = i + 1
-                if (canRemoveWholeContent) {
-                  canRemoveWholeContent = false
-                  while (i > prevStart) {
-                    queueRemoveVNode(prevChildren[prevStart++], container)
-                  }
-                }
-                if (pos > j) {
-                  moved = true
-                } else {
-                  pos = j
-                }
-                patch(prevVNode, nextVNode, container, contextVNode, isSVG)
-                patched++
-                break
-              }
-            }
-            if (!canRemoveWholeContent && j > nextEnd) {
-              queueRemoveVNode(prevVNode, container)
-            }
-          } else if (!canRemoveWholeContent) {
-            queueRemoveVNode(prevVNode, container)
-          }
-        }
-      } else {
-        const keyIndex: Record<string, number> = {}
-
-        // Map keys by their index
-        for (i = nextStart; i <= nextEnd; i++) {
-          keyIndex[nextChildren[i].key as string] = i
-        }
-
-        // Try to patch same keys
-        for (i = prevStart; i <= prevEnd; i++) {
-          prevVNode = prevChildren[i]
 
-          if (patched < nextLeft) {
-            j = keyIndex[prevVNode.key as string]
-
-            if (j !== void 0) {
-              if (canRemoveWholeContent) {
-                canRemoveWholeContent = false
-                while (i > prevStart) {
-                  queueRemoveVNode(prevChildren[prevStart++], container)
-                }
-              }
-              nextVNode = getNextVNode(nextChildren, j)
-              sources[j - nextStart] = i + 1
-              if (pos > j) {
-                moved = true
-              } else {
-                pos = j
-              }
-              patch(prevVNode, nextVNode, container, contextVNode, isSVG)
-              patched++
-            } else if (!canRemoveWholeContent) {
-              queueRemoveVNode(prevVNode, container)
-            }
-          } else if (!canRemoveWholeContent) {
-            queueRemoveVNode(prevVNode, container)
+      // style
+      // this flag is matched when the element has dynamic style bindings
+      // TODO separate static and dynamic styles?
+      if (patchFlag & STYLE) {
+        setStyles(el.style, oldProps.style, newProps.style)
+      }
+
+      // props
+      // This flag is matched when the element has dynamic prop/attr bindings
+      // other than class and style. The keys of dynamic prop/attrs are saved for
+      // faster iteration.
+      // Note dynamic keys like :[foo]="bar" will cause this optimization to
+      // bail out and go through a full diff because we need to unset the old key
+      if (patchFlag & PROPS) {
+        const propsToUpdate = n2.dynamicProps
+        for (let i = 0; i < propsToUpdate.length; i++) {
+          const key = propsToUpdate[i]
+          const prev = oldProps[key]
+          const next = newProps[key]
+          if (prev !== next) {
+            hostPatchProp(el, key, next, prev)
           }
         }
       }
-      // fast-path: if nothing patched remove all old and add all new
-      if (canRemoveWholeContent) {
-        queueRemoveChildren(prevChildren as MountedVNode[], container, endNode)
-        mountArrayChildren(
-          nextChildren,
-          container,
-          contextVNode,
-          isSVG,
-          endNode
-        )
-      } else {
-        if (moved) {
-          const seq = lis(sources)
-          j = seq.length - 1
-          for (i = nextLeft - 1; i >= 0; i--) {
-            if (sources[i] === 0) {
-              pos = i + nextStart
-              nextVNode = getNextVNode(nextChildren, pos)
-              nextPos = pos + 1
-              mount(
-                nextVNode,
-                container,
-                contextVNode,
-                isSVG,
-                nextPos < nextLength ? nextChildren[nextPos].el : endNode
-              )
-            } else if (j < 0 || i !== seq[j]) {
-              pos = i + nextStart
-              nextVNode = nextChildren[pos]
-              nextPos = pos + 1
-              insertVNode(
-                nextVNode as MountedVNode,
-                container,
-                nextPos < nextLength ? nextChildren[nextPos].el : endNode
-              )
-            } else {
-              j--
-            }
-          }
-        } else if (patched !== nextLeft) {
-          // when patched count doesn't match b length we need to insert those
-          // new ones loop backwards so we can use insertBefore
-          for (i = nextLeft - 1; i >= 0; i--) {
-            if (sources[i] === 0) {
-              pos = i + nextStart
-              nextVNode = getNextVNode(nextChildren, pos)
-              nextPos = pos + 1
-              mount(
-                nextVNode,
-                container,
-                contextVNode,
-                isSVG,
-                nextPos < nextLength ? nextChildren[nextPos].el : endNode
-              )
-            }
-          }
+
+      // text
+      // This flag is matched when the element has only dynamic text children.
+      // this flag is terminal (i.e. skips children diffing).
+      if (patchFlag & TEXT) {
+        if (n1.children !== n2.children) {
+          hostSetElementText(el, n2.children)
         }
+        return // terminal
       }
+    } else if (!optimized) {
+      // unoptimized, full diff
+      patchProps(el, oldProps, newProps)
     }
-  }
 
-  function insertVNode(
-    vnode: MountedVNode,
-    container: RenderNode,
-    refNode: RenderNode | null
-  ) {
-    const { flags, childFlags, children } = vnode
-    if (flags & VNodeFlags.FRAGMENT) {
-      switch (childFlags) {
-        case ChildrenFlags.SINGLE_VNODE:
-          insertVNode(children as MountedVNode, container, refNode)
-          break
-        case ChildrenFlags.NO_CHILDREN:
-          break
-        default:
-          for (let i = 0; i < (children as MountedVNode[]).length; i++) {
-            insertVNode((children as MountedVNode[])[i], container, refNode)
-          }
+    if (dynamicChildren != null) {
+      // children fast path
+      const olddynamicChildren = n1.dynamicChildren
+      for (let i = 0; i < dynamicChildren.length; i++) {
+        patch(olddynamicChildren[i], dynamicChildren[i], el, null, true)
       }
-    } else {
-      queueInsertOrAppend(container, vnode.el as RenderNode, refNode)
+    } else if (!optimized) {
+      // full diff
+      patchChildren(n1, n2, el)
     }
   }
 
-  // unmounting ----------------------------------------------------------------
-
-  function unmount(vnode: MountedVNode) {
-    const { flags, data, children, childFlags, ref, handle } = vnode
-    const isElement = flags & VNodeFlags.ELEMENT
-    if (isElement || flags & VNodeFlags.FRAGMENT) {
-      if (isElement && data != null && data.vnodeBeforeUnmount) {
-        data.vnodeBeforeUnmount(vnode)
-      }
-      unmountChildren(children as VNodeChildren, childFlags)
-      if (isElement && data != null && data.vnodeUnmounted) {
-        data.vnodeUnmounted(vnode)
-      }
-    } else if (flags & VNodeFlags.COMPONENT) {
-      if (flags & VNodeFlags.COMPONENT_STATEFUL) {
-        if (flags & VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE) {
-          deactivateComponentInstance(children as ComponentInstance)
-        } else {
-          unmountComponentInstance(children as ComponentInstance)
+  function patchProps(el, oldProps, newProps) {
+    if (oldProps !== newProps) {
+      for (const key in newProps) {
+        const next = newProps[key]
+        const prev = oldProps[key]
+        if (next !== prev) {
+          hostPatchProp(el, key, next, prev)
         }
-      } else {
-        // functional
-        stopReactiveEffect((handle as FunctionalHandle).update)
-        unmount(children as MountedVNode)
       }
-    } else if (flags & VNodeFlags.PORTAL) {
-      if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
-        queueRemoveChildren(
-          children as MountedVNode[],
-          vnode.tag as RenderNode,
-          null
-        )
-      } else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
-        queueRemoveVNode(children as MountedVNode, vnode.tag as RenderNode)
-      }
-    }
-    if (ref) {
-      ref(null)
-    }
-  }
-
-  function unmountChildren(children: VNodeChildren, childFlags: ChildrenFlags) {
-    if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
-      unmountArrayChildren(children as MountedVNode[])
-    } else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
-      unmount(children as MountedVNode)
-    }
-  }
-
-  function unmountArrayChildren(children: MountedVNode[]) {
-    for (let i = 0; i < children.length; i++) {
-      unmount(children[i])
-    }
-  }
-
-  function queueRemoveVNode(vnode: MountedVNode, container: RenderNode) {
-    queueNodeOp([removeVNode, vnode, container])
-  }
-
-  function removeVNode(vnode: MountedVNode, container: RenderNode) {
-    unmount(vnode)
-    const { el, flags, children, childFlags } = vnode
-    if (container && el) {
-      if (flags & VNodeFlags.FRAGMENT) {
-        switch (childFlags) {
-          case ChildrenFlags.SINGLE_VNODE:
-            removeVNode(children as MountedVNode, container)
-            break
-          case ChildrenFlags.NO_CHILDREN:
-            platformRemoveChild(container, el)
-            break
-          default:
-            for (let i = 0; i < (children as MountedVNode[]).length; i++) {
-              removeVNode((children as MountedVNode[])[i], container)
-            }
+      if (oldProps !== emptyObj) {
+        for (const key in oldProps) {
+          if (!(key in newProps)) {
+            hostPatchProp(el, key, null, null)
+          }
         }
-      } else {
-        platformRemoveChild(container, el)
       }
-      ;(vnode as any).el = null
     }
   }
 
-  function queueRemoveChildren(
-    children: MountedVNode[],
-    container: RenderNode,
-    refNode: RenderNode | null
-  ) {
-    queueNodeOp([removeChildren, children, container, refNode])
-  }
-
-  function removeChildren(
-    children: MountedVNode[],
-    container: RenderNode,
-    refNode: RenderNode | null
-  ) {
-    unmountArrayChildren(children)
-    if (refNode === null) {
-      platformClearContent(container)
+  function processFragment(n1, n2, container, anchor, optimized) {
+    const fragmentAnchor = (n2.el = n1 ? n1.el : document.createComment(''))
+    if (n1 == null) {
+      insert(fragmentAnchor, container, anchor)
+      mountChildren(n2.children, container, fragmentAnchor)
     } else {
-      for (let i = 0; i < children.length; i++) {
-        removeVNode(children[i], container)
-      }
+      patchChildren(n1, n2, container, fragmentAnchor, optimized)
     }
   }
 
-  // Component lifecycle -------------------------------------------------------
-
-  function mountComponentInstance(
-    vnode: VNode,
-    container: RenderNode | null,
-    isSVG: boolean,
-    endNode: RenderNode | null
-  ): Function {
-    if (__DEV__) {
-      pushWarningContext(vnode)
-    }
-
-    // a vnode may already have an instance if this is a compat call with
-    // new Vue()
-    const instance = ((__COMPAT__ && vnode.children) ||
-      (vnode.children = createComponentInstance(
-        vnode as any
-      ))) as ComponentInstance
+  function patchChildren(n1, n2, container, anchor, optimized) {
+    const c1 = n1 && n1.children
+    const c2 = n2.children
 
-    // inject platform-specific unmount to keep-alive container
-    if ((vnode.tag as any)[KeepAliveSymbol] === true) {
-      ;(instance as any).$unmount = unmountComponentInstance
-    }
-
-    const {
-      $proxy,
-      $options: { beforeMount, mounted, renderTracked, renderTriggered }
-    } = instance
-
-    instance.$forceUpdate = () => {
-      queueJob(instance._update)
-    }
-
-    const effectOptions: ReactiveEffectOptions = {
-      scheduler: queueJob
-    }
-
-    if (__DEV__) {
-      if (renderTracked) {
-        effectOptions.onTrack = event => {
-          callLifecycleHookWithHandler(
-            renderTracked,
-            $proxy,
-            ErrorTypes.RENDER_TRACKED,
-            event
-          )
-        }
-      }
-      if (renderTriggered) {
-        effectOptions.onTrigger = event => {
-          callLifecycleHookWithHandler(
-            renderTriggered,
-            $proxy,
-            ErrorTypes.RENDER_TRIGGERED,
-            event
-          )
-        }
-      }
-    }
-
-    instance._update = createReactiveEffect(() => {
-      if (instance._unmounted) {
+    // fast path
+    const { patchFlag } = n2
+    if (patchFlag != null) {
+      if (patchFlag & KEYED) {
+        // this could be either fully-keyed or mixed (some keyed some not)
+        patchKeyedChildren(c1, c2, container, anchor, optimized)
+        return
+      } else if (patchFlag & UNKEYED) {
+        // unkeyed
+        patchUnkeyedChildren(c1, c2, container, anchor, optimized)
         return
       }
-      if (instance._mounted) {
-        updateComponentInstance(instance, isSVG)
-      } else {
-        if (beforeMount) {
-          callLifecycleHookWithHandler(
-            beforeMount,
-            $proxy,
-            ErrorTypes.BEFORE_MOUNT
-          )
-        }
-
-        instance.$vnode = renderInstanceRoot(instance) as MountedVNode
-
-        queuePostEffect(() => {
-          vnode.el = instance.$vnode.el
-          if (__COMPAT__) {
-            // expose __vue__ for devtools
-            ;(vnode.el as any).__vue__ = instance
-          }
-          if (vnode.ref) {
-            vnode.ref($proxy)
-          }
-          if (mounted) {
-            callLifecycleHookWithHandler(mounted, $proxy, ErrorTypes.MOUNTED)
-          }
-          instance._mounted = true
-        })
-
-        mount(instance.$vnode, container, vnode as MountedVNode, isSVG, endNode)
-      }
-    }, effectOptions)
-
-    if (__DEV__) {
-      popWarningContext()
-    }
-
-    // cleanup if mount is invalidated before committed
-    return () => {
-      teardownComponentInstance(instance)
-    }
-  }
-
-  function updateComponentInstance(
-    instance: ComponentInstance,
-    isSVG: boolean
-  ) {
-    if (__DEV__ && instance.$parentVNode) {
-      pushWarningContext(instance.$parentVNode as VNode)
-    }
-
-    const {
-      $vnode: prevVNode,
-      $parentVNode,
-      $proxy,
-      $options: { beforeUpdate }
-    } = instance
-    if (beforeUpdate) {
-      callLifecycleHookWithHandler(
-        beforeUpdate,
-        $proxy,
-        ErrorTypes.BEFORE_UPDATE,
-        prevVNode
-      )
     }
 
-    const nextVNode = renderInstanceRoot(instance) as MountedVNode
-
-    queuePostEffect(() => {
-      instance.$vnode = nextVNode
-      const el = nextVNode.el as RenderNode
-      if (__COMPAT__) {
-        // expose __vue__ for devtools
-        ;(el as any).__vue__ = instance
-      }
-      // recursively update contextVNode el for nested HOCs
-      if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
-        let vnode = $parentVNode
-        while (vnode !== null) {
-          if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
-            vnode.el = el
-          }
-          vnode = vnode.contextVNode
-        }
+    if (typeof c2 === 'string') {
+      // text children fast path
+      if (Array.isArray(c1)) {
+        unmountChildren(c1, false)
       }
-      const { updated } = instance.$options
-      if (updated) {
-        callLifecycleHookWithHandler(
-          updated,
-          $proxy,
-          ErrorTypes.UPDATED,
-          nextVNode
-        )
+      hostSetElementText(container, c2)
+    } else {
+      if (typeof c1 === 'string') {
+        hostSetElementText('')
+        mountChildren(c2, container, anchor)
+      } else {
+        // two arrays, cannot assume anything, do full diff
+        patchKeyedChildren(c1, c2, container, anchor, optimized)
       }
-
-      // TODO fix me
-      // if (vnodeUpdatedHooks.length > 0) {
-      //   const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
-      //   vnodeUpdatedHooks.length = 0
-      //   for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
-      //     vnodeUpdatedHooksForCurrentInstance[i]()
-      //   }
-      // }
-    })
-
-    const container = platformParentNode(prevVNode.el) as RenderNode
-    patch(prevVNode, nextVNode, container, $parentVNode as MountedVNode, isSVG)
-
-    if (__DEV__ && instance.$parentVNode) {
-      popWarningContext()
-    }
-  }
-
-  function unmountComponentInstance(instance: ComponentInstance) {
-    if (instance._unmounted) {
-      return
-    }
-    const {
-      $vnode,
-      $proxy,
-      $options: { beforeUnmount, unmounted }
-    } = instance
-    if (beforeUnmount) {
-      callLifecycleHookWithHandler(
-        beforeUnmount,
-        $proxy,
-        ErrorTypes.BEFORE_UNMOUNT
-      )
-    }
-    if ($vnode) {
-      unmount($vnode)
-    }
-    teardownComponentInstance(instance)
-    instance._unmounted = true
-    if (unmounted) {
-      callLifecycleHookWithHandler(unmounted, $proxy, ErrorTypes.UNMOUNTED)
-    }
-  }
-
-  // Keep Alive ----------------------------------------------------------------
-
-  function activateComponentInstance(
-    vnode: VNode,
-    container: RenderNode | null,
-    endNode: RenderNode | null
-  ) {
-    if (__DEV__) {
-      pushWarningContext(vnode)
     }
-    const instance = vnode.children as ComponentInstance
-    vnode.el = instance.$el as RenderNode
-    if (container != null) {
-      insertVNode(instance.$vnode, container, endNode)
-    }
-    if (__DEV__) {
-      popWarningContext()
-    }
-    queuePostEffect(() => {
-      callActivatedHook(instance, true)
-    })
   }
 
-  function callActivatedHook(instance: ComponentInstance, asRoot: boolean) {
-    // 1. check if we are inside an inactive parent tree.
-    if (asRoot) {
-      instance._inactiveRoot = false
-      if (isInInactiveTree(instance)) return
+  function patchUnkeyedChildren(c1, c2, container, anchor, optimized) {
+    c1 = c1 || emptyArr
+    c2 = c2 || emptyArr
+    const oldLength = c1.length
+    const newLength = c2.length
+    const commonLength = Math.min(oldLength, newLength)
+    let i
+    for (i = 0; i < commonLength; i++) {
+      const nextChild = (c2[i] = normalizeChild(c2[i]))
+      patch(c1[i], nextChild, container, null, optimized)
     }
-    if (asRoot || !instance._inactiveRoot) {
-      // 2. recursively call activated on child tree, depth-first
-      const {
-        $children,
-        $proxy,
-        $options: { activated }
-      } = instance
-      for (let i = 0; i < $children.length; i++) {
-        callActivatedHook($children[i], false)
-      }
-      if (activated) {
-        callLifecycleHookWithHandler(activated, $proxy, ErrorTypes.ACTIVATED)
-      }
+    if (oldLength > newLength) {
+      // remove old
+      unmountChildren(c1, commonLength, true)
+    } else {
+      // mount new
+      mountChildren(c2, container, anchor, commonLength)
     }
   }
 
-  function deactivateComponentInstance(instance: ComponentInstance) {
-    callDeactivateHook(instance, true)
+  // can be all-keyed or mixed
+  function patchKeyedChildren(c1, c2, container, anchor, optimized) {
+    // TODO
+    patchUnkeyedChildren(c1, c2, container, anchor, optimized)
   }
 
-  function callDeactivateHook(instance: ComponentInstance, asRoot: boolean) {
-    if (asRoot) {
-      instance._inactiveRoot = true
-      if (isInInactiveTree(instance)) return
-    }
-    if (asRoot || !instance._inactiveRoot) {
-      // 2. recursively call deactivated on child tree, depth-first
-      const {
-        $children,
-        $proxy,
-        $options: { deactivated }
-      } = instance
-      for (let i = 0; i < $children.length; i++) {
-        callDeactivateHook($children[i], false)
-      }
-      if (deactivated) {
-        callLifecycleHookWithHandler(
-          deactivated,
-          $proxy,
-          ErrorTypes.DEACTIVATED
-        )
+  function unmount(vnode, doRemove) {
+    if (doRemove) {
+      if (vnode.type === Fragment) {
+        unmountChildren(vnode.children, 0, doRemove)
       }
+      remove(vnode.el)
     }
-  }
-
-  function isInInactiveTree(instance: ComponentInstance): boolean {
-    while ((instance = instance.$parent as any) !== null) {
-      if (instance._inactiveRoot) return true
+    if (Array.isArray(vnode.children)) {
+      unmountChildren(vnode.children)
     }
-    return false
   }
 
-  // TODO hydrating ------------------------------------------------------------
-
-  // API -----------------------------------------------------------------------
-
-  function render(vnode: VNode | null, container: any) {
-    const prevVNode = container.vnode
-    if (vnode && vnode.el) {
-      vnode = cloneVNode(vnode)
-    }
-    if (prevVNode == null) {
-      if (vnode) {
-        mount(vnode, container, null, false, null)
-        container.vnode = vnode
-      }
-    } else {
-      if (vnode) {
-        patch(prevVNode, vnode, container, null, false)
-        container.vnode = vnode
-      } else {
-        queueRemoveVNode(prevVNode, container)
-        container.vnode = null
-      }
-    }
-    if (__COMPAT__) {
-      flushEffects()
-      return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
-        ? (vnode.children as ComponentInstance).$proxy
-        : null
-    } else {
-      return nextTick(() => {
-        return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
-          ? (vnode.children as ComponentInstance).$proxy
-          : null
-      })
+  function unmountChildren(children, start = 0, doRemove) {
+    for (let i = start; i < children.length; i++) {
+      unmount(children[i], doRemove)
     }
   }
 
-  return { render }
-}
-
-// Utils -----------------------------------------------------------------------
-
-// retrieves a vnode from a children array, making sure to clone it if the
-// vnode is already mounted.
-function getNextVNode(ownerArray: VNode[], index: number): VNode {
-  const vnode = ownerArray[index]
-  return vnode.el === null ? vnode : (ownerArray[index] = cloneVNode(vnode))
-}
-
-// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
-function lis(arr: number[]): number[] {
-  const p = arr.slice()
-  const result = [0]
-  let i
-  let j
-  let u
-  let v
-  let c
-  const len = arr.length
-  for (i = 0; i < len; i++) {
-    const arrI = arr[i]
-    if (arrI !== 0) {
-      j = result[result.length - 1]
-      if (arr[j] < arrI) {
-        p[i] = j
-        result.push(i)
-        continue
-      }
-      u = 0
-      v = result.length - 1
-      while (u < v) {
-        c = ((u + v) / 2) | 0
-        if (arr[result[c]] < arrI) {
-          u = c + 1
-        } else {
-          v = c
-        }
-      }
-      if (arrI < arr[result[u]]) {
-        if (u > 0) {
-          p[i] = result[u - 1]
-        }
-        result[u] = i
-      }
-    }
-  }
-  u = result.length
-  v = result[u - 1]
-  while (u-- > 0) {
-    result[u] = v
-    v = p[v]
+  return function render(vnode, dom) {
+    patch(dom._vnode, vnode, dom)
+    return (dom._vnode = vnode)
   }
-  return result
 }
diff --git a/packages/runtime-core/src/errorHandling.ts b/packages/runtime-core/src/errorHandling.ts
deleted file mode 100644 (file)
index 2a0abd8..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-import { ComponentInstance } from './component'
-import { warn, pushWarningContext, popWarningContext } from './warning'
-import { VNode } from './vdom'
-import { VNodeFlags } from './flags'
-import { ComponentProxy } from './componentProxy'
-
-export const enum ErrorTypes {
-  BEFORE_CREATE = 1,
-  CREATED,
-  BEFORE_MOUNT,
-  MOUNTED,
-  BEFORE_UPDATE,
-  UPDATED,
-  BEFORE_UNMOUNT,
-  UNMOUNTED,
-  ACTIVATED,
-  DEACTIVATED,
-  ERROR_CAPTURED,
-  RENDER,
-  RENDER_TRACKED,
-  RENDER_TRIGGERED,
-  WATCH_CALLBACK,
-  NATIVE_EVENT_HANDLER,
-  COMPONENT_EVENT_HANDLER,
-  SCHEDULER
-}
-
-const ErrorTypeStrings: Record<number, string> = {
-  [ErrorTypes.BEFORE_CREATE]: 'in beforeCreate lifecycle hook',
-  [ErrorTypes.CREATED]: 'in created lifecycle hook',
-  [ErrorTypes.BEFORE_MOUNT]: 'in beforeMount lifecycle hook',
-  [ErrorTypes.MOUNTED]: 'in mounted lifecycle hook',
-  [ErrorTypes.BEFORE_UPDATE]: 'in beforeUpdate lifecycle hook',
-  [ErrorTypes.UPDATED]: 'in updated lifecycle hook',
-  [ErrorTypes.BEFORE_UNMOUNT]: 'in beforeUnmount lifecycle hook',
-  [ErrorTypes.UNMOUNTED]: 'in unmounted lifecycle hook',
-  [ErrorTypes.ACTIVATED]: 'in activated lifecycle hook',
-  [ErrorTypes.DEACTIVATED]: 'in deactivated lifecycle hook',
-  [ErrorTypes.ERROR_CAPTURED]: 'in errorCaptured lifecycle hook',
-  [ErrorTypes.RENDER]: 'in render function',
-  [ErrorTypes.RENDER_TRACKED]: 'in renderTracked debug hook',
-  [ErrorTypes.RENDER_TRIGGERED]: 'in renderTriggered debug hook',
-  [ErrorTypes.WATCH_CALLBACK]: 'in watcher callback',
-  [ErrorTypes.NATIVE_EVENT_HANDLER]: 'in native event handler',
-  [ErrorTypes.COMPONENT_EVENT_HANDLER]: 'in component event handler',
-  [ErrorTypes.SCHEDULER]:
-    'when flushing updates. This may be a Vue internals bug.'
-}
-
-export function callLifecycleHookWithHandler(
-  hook: Function,
-  instanceProxy: ComponentProxy,
-  type: ErrorTypes,
-  arg?: any
-) {
-  try {
-    const res = hook.call(instanceProxy, arg)
-    if (res && !res._isVue && typeof res.then === 'function') {
-      ;(res as Promise<any>).catch(err => {
-        handleError(err, instanceProxy._self, type)
-      })
-    }
-  } catch (err) {
-    handleError(err, instanceProxy._self, type)
-  }
-}
-
-export function handleError(
-  err: Error,
-  instance: ComponentInstance | VNode | null,
-  type: ErrorTypes
-) {
-  const isFunctional = instance && (instance as VNode)._isVNode
-  const contextVNode =
-    instance &&
-    ((isFunctional
-      ? instance
-      : (instance as ComponentInstance).$parentVNode) as VNode | null)
-  let cur: ComponentInstance | null = null
-  if (isFunctional) {
-    let vnode = instance as VNode | null
-    while (vnode && !(vnode.flags & VNodeFlags.COMPONENT_STATEFUL)) {
-      vnode = vnode.contextVNode
-    }
-    if (vnode) {
-      cur = vnode.children as ComponentInstance
-    }
-  } else if (instance) {
-    const parent = (instance as ComponentInstance).$parent
-    cur = parent && parent._self
-  }
-  while (cur) {
-    const handler = cur.errorCaptured
-    if (handler) {
-      try {
-        const captured = handler.call(
-          cur,
-          err,
-          type,
-          isFunctional ? null : instance
-        )
-        if (captured) return
-      } catch (err2) {
-        logError(err2, ErrorTypes.ERROR_CAPTURED, contextVNode)
-      }
-    }
-    cur = cur.$parent && cur.$parent._self
-  }
-  logError(err, type, contextVNode)
-}
-
-function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
-  if (__DEV__) {
-    const info = ErrorTypeStrings[type]
-    if (contextVNode) {
-      pushWarningContext(contextVNode)
-    }
-    if (/private field/.test(err.message)) {
-      warn(
-        `Private fields cannot be accessed directly on \`this\` in a component ` +
-          `class because they cannot be tunneled through Proxies. ` +
-          `Use \`this._self.#field\` instead.`
-      )
-    } else {
-      warn(`Unhandled error${info ? ` ${info}` : ``}`)
-    }
-    console.error(err)
-    if (contextVNode) {
-      popWarningContext()
-    }
-  } else {
-    throw err
-  }
-}
diff --git a/packages/runtime-core/src/flags.ts b/packages/runtime-core/src/flags.ts
deleted file mode 100644 (file)
index 159bd9a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-// vnode flags
-export const enum VNodeFlags {
-  ELEMENT_HTML = 1,
-  ELEMENT_SVG = 1 << 1,
-
-  COMPONENT_STATEFUL_NORMAL = 1 << 2,
-  COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE = 1 << 3,
-  COMPONENT_STATEFUL_KEPT_ALIVE = 1 << 4,
-  COMPONENT_FUNCTIONAL = 1 << 5,
-
-  TEXT = 1 << 6,
-  FRAGMENT = 1 << 7,
-  PORTAL = 1 << 8,
-
-  // masks (only use for bitwise checks, do not use equal checks or assign)
-  ELEMENT = ELEMENT_HTML | ELEMENT_SVG,
-  COMPONENT_STATEFUL = COMPONENT_STATEFUL_NORMAL |
-    COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE |
-    COMPONENT_STATEFUL_KEPT_ALIVE,
-  COMPONENT = COMPONENT_STATEFUL | COMPONENT_FUNCTIONAL
-}
-
-export const enum ChildrenFlags {
-  UNKNOWN_CHILDREN = 0,
-  NO_CHILDREN = 1,
-  SINGLE_VNODE = 1 << 1,
-  KEYED_VNODES = 1 << 2,
-  NONE_KEYED_VNODES = 1 << 3,
-  STABLE_SLOTS = 1 << 4,
-  DYNAMIC_SLOTS = 1 << 5,
-
-  // masks
-  HAS_SLOTS = STABLE_SLOTS | DYNAMIC_SLOTS,
-  MULTIPLE_VNODES = KEYED_VNODES | NONE_KEYED_VNODES
-}
index c2dc3be491b9642fcc8aa7fae5d8c863ae49bee7..095191c9a3f89b9f8f70ecbf46fa28a8fb2dbfac 100644 (file)
-import { ChildrenFlags } from './flags'
-import { ComponentClass, FunctionalComponent, Component } from './component'
-import { ComponentOptions } from './componentOptions'
-import {
-  VNode,
-  createElementVNode,
-  createComponentVNode,
-  createFragment,
-  createPortal,
-  VNodeData,
-  BuiltInProps,
-  Key
-} from './vdom'
-import { isObservable } from '@vue/observer'
-import { warn } from './warning'
-import { isString, isArray, isFunction, isObject } from '@vue/shared'
+export const Fragment = Symbol('Fragment')
+export const Text = Symbol('Text')
+export const Empty = Symbol('Empty')
 
-export const Fragment = Symbol()
-export const Portal = Symbol()
+type VNodeTypes =
+  | string
+  | Function
+  | typeof Fragment
+  | typeof Text
+  | typeof Empty
 
-type RawChildType = VNode | string | number | boolean | null | undefined
+export type VNodeChild = VNode | string | number | null
+export interface VNodeChildren extends Array<VNodeChildren | VNodeChild> {}
 
-export type RawSlots = {
-  $stable?: boolean
-  [name: string]: RawChildType | (() => RawChildrenType)
+export interface VNode {
+  type: VNodeTypes
+  props: { [key: string]: any } | null
+  key: string | number | null
+  children: string | VNodeChildren | null
+  patchFlag: number | null
+  dynamicProps: string[] | null
+  dynamicChildren: VNode[] | null
 }
 
-export type RawChildrenType = RawChildType | RawChildType[]
-
-export type ElementType =
-  | string
-  | FunctionalComponent
-  | ComponentClass
-  | ComponentOptions
-  | typeof Fragment
-  | typeof Portal
+const blockStack: (VNode[])[] = []
 
-// This is used to differentiate the data object from
-// vnodes and arrays
-type Differ = { _isVNode?: never; [Symbol.iterator]?: never }
+// open block
+export function openBlock() {
+  blockStack.push([])
+}
 
-type OptionsComponent<P> =
-  | (ComponentOptions<P> & { template: string })
-  | (ComponentOptions<P> & { render: Function })
+let shouldTrack = true
 
-// TODO improve return type with props information
-interface createElement {
-  // element
-  (tag: string, children?: RawChildrenType): VNode
-  (
-    tag: string,
-    // TODO support native element properties
-    data?: VNodeData & Differ | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
-  // fragment
-  (tag: typeof Fragment, children?: RawChildrenType): VNode
-  (
-    tag: typeof Fragment,
-    data?: ({ key?: Key } & Differ) | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
-  // portal
-  (tag: typeof Portal, children?: RawChildrenType): VNode
-  (
-    tag: typeof Portal,
-    data?: ({ target: any } & BuiltInProps & Differ) | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
-  // object
-  <P>(tag: OptionsComponent<P>, children?: RawChildrenType): VNode
-  <P>(
-    tag: OptionsComponent<P>,
-    data?: (P & BuiltInProps & Differ) | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
-  // functional
-  <P>(tag: FunctionalComponent<P>, children?: RawChildrenType): VNode
-  <P>(
-    tag: FunctionalComponent<P>,
-    data?: (P & BuiltInProps & Differ) | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
-  // class
-  <P>(tag: new () => Component<P>, children?: RawChildrenType): VNode
-  <P>(
-    tag: new () => Component<P>,
-    data?: (P & BuiltInProps & Differ) | null,
-    children?: RawChildrenType | RawSlots
-  ): VNode
+// block
+export function createBlock(
+  type: VNodeTypes,
+  props?: { [key: string]: any } | null,
+  children?: any,
+  patchFlag?: number,
+  dynamicProps?: string[]
+): VNode {
+  // avoid a block with optFlag tracking itself
+  shouldTrack = false
+  const vnode = createVNode(type, props, children, patchFlag, dynamicProps)
+  shouldTrack = true
+  vnode.dynamicChildren = blockStack.pop() || null
+  // a block is always going to be patched
+  trackDynamicNode(vnode)
+  return vnode
 }
 
-export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
-  if (data !== null && (isArray(data) || !isObject(data) || data._isVNode)) {
-    children = data
-    data = null
+// element
+export function createVNode(
+  type: VNodeTypes,
+  props: { [key: string]: any } | null = null,
+  children: any = null,
+  patchFlag: number | null = null,
+  dynamicProps: string[] | null = null
+): VNode {
+  const vnode: VNode = {
+    type,
+    props,
+    key: props && props.key,
+    children,
+    patchFlag,
+    dynamicProps,
+    dynamicChildren: null
   }
-
-  if (data === void 0) data = null
-  if (children === void 0) children = null
-
-  // if value is observable, create a clone of original
-  // so that we can normalize its class/style
-  // since this guard is only placed here, this means any direct createXXXVnode
-  // functions only accept fresh data objects.
-  if (isObservable(data)) {
-    data = Object.assign({}, data)
+  if (patchFlag != null && shouldTrack) {
+    trackDynamicNode(vnode)
   }
+  return vnode
+}
 
-  let key = null
-  let ref = null
-  let portalTarget = null
-  if (data != null) {
-    if (data.slots != null) {
-      children = data.slots
-    }
-    if (data.key != null) {
-      ;({ key } = data)
-    }
-    if (data.ref != null) {
-      ;({ ref } = data)
-    }
-    if (data.target != null) {
-      portalTarget = data.target
-    }
+function trackDynamicNode(vnode: VNode) {
+  const currentBlockDynamicNodes = blockStack[blockStack.length - 1]
+  if (currentBlockDynamicNodes) {
+    currentBlockDynamicNodes.push(vnode)
   }
+}
 
-  if (isString(tag)) {
-    // element
-    return createElementVNode(
-      tag,
-      data,
-      children,
-      ChildrenFlags.UNKNOWN_CHILDREN,
-      key,
-      ref
-    )
-  } else if (tag === Fragment) {
-    if (__DEV__ && ref) {
-      warn('Ref cannot be used on Fragments. Use it on inner elements instead.')
-    }
-    return createFragment(children, ChildrenFlags.UNKNOWN_CHILDREN, key)
-  } else if (tag === Portal) {
-    if (__DEV__ && !portalTarget) {
-      warn('Portal must have a target: ', portalTarget)
-    }
-    return createPortal(
-      portalTarget,
-      children,
-      ChildrenFlags.UNKNOWN_CHILDREN,
-      key,
-      ref
-    )
-  } else {
-    if (__DEV__ && !isFunction(tag) && !isObject(tag)) {
-      warn('Invalid component passed to h(): ', tag)
-    }
-    // component
-    return createComponentVNode(
-      tag,
-      data,
-      children,
-      ChildrenFlags.UNKNOWN_CHILDREN,
-      key,
-      ref
-    )
-  }
-}) as createElement
+export function cloneVNode(vnode: VNode): VNode {
+  // TODO
+}
index be2cb6cc39d1f341f14d1c07e8fbb925846c8882..1ea0a177767e1f1e1e3ed2c550d1b019d6b6eb01 100644 (file)
@@ -1,41 +1,5 @@
-// Core API
-export { h, Fragment, Portal } from './h'
-export { Component } from './component'
-export {
-  cloneVNode,
-  createElementVNode,
-  createComponentVNode,
-  createTextVNode,
-  createFragment,
-  createPortal
-} from './vdom'
-export {
-  createRenderer,
-  NodeOps,
-  PatchDataFunction,
-  RendererOptions
-} from './createRenderer'
+export { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
 
-// Observer API
-export * from '@vue/observer'
+export { openBlock, createBlock, createVNode, Fragment, Text, Empty } from './h'
 
-// Scheduler API
-export { nextTick } from '@vue/scheduler'
-
-// Optional APIs
-// these are imported on-demand and can be tree-shaken
-export { createAsyncComponent } from './optional/asyncComponent'
-export { KeepAlive } from './optional/keepAlive'
-export { applyDirectives } from './optional/directives'
-export { mixins } from './optional/mixins'
-export { EventEmitter } from './optional/eventEmitter'
-export { memoize } from './optional/memoize'
-
-// flags & types
-export { ComponentType, ComponentClass, FunctionalComponent } from './component'
-export { VNodeFlags, ChildrenFlags } from './flags'
-export { VNode, Slots } from './vdom'
-
-// Internal API, for libraries or renderers that need to perform low level work
-export * from './componentOptions'
-export { createComponentInstance } from './componentInstance'
+export { createRenderer } from './createRenderer'
diff --git a/packages/runtime-core/src/optional/asyncComponent.ts b/packages/runtime-core/src/optional/asyncComponent.ts
deleted file mode 100644 (file)
index 6192057..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-import { ChildrenFlags } from '../flags'
-import { createComponentVNode, Slots } from '../vdom'
-import { Component, ComponentType, ComponentClass } from '../component'
-import { unwrap } from '@vue/observer'
-import { isFunction } from '@vue/shared'
-
-interface AsyncComponentFactory {
-  (): Promise<ComponentType>
-  resolved?: ComponentType
-}
-
-interface AsyncComponentFullOptions {
-  factory: AsyncComponentFactory
-  loading?: ComponentType
-  error?: ComponentType
-  delay?: number
-  timeout?: number
-}
-
-type AsyncComponentOptions = AsyncComponentFactory | AsyncComponentFullOptions
-
-export function createAsyncComponent(
-  options: AsyncComponentOptions
-): ComponentClass {
-  if (isFunction(options)) {
-    options = { factory: options }
-  }
-
-  const {
-    factory,
-    timeout,
-    delay = 200,
-    loading: loadingComp,
-    error: errorComp
-  } = options
-
-  return class AsyncContainer extends Component {
-    comp: ComponentType | null = null
-    err: Error | null = null
-    delayed: boolean = false
-    timedOut: boolean = false
-
-    // doing this in beforeMount so this is non-SSR only
-    beforeMount() {
-      if (factory.resolved) {
-        this.comp = factory.resolved
-      } else {
-        factory()
-          .then(resolved => {
-            this.comp = factory.resolved = resolved
-          })
-          .catch(err => {
-            this.err = err
-          })
-      }
-      if (timeout != null) {
-        setTimeout(() => {
-          this.timedOut = true
-        }, timeout)
-      }
-      if (delay != null) {
-        this.delayed = true
-        setTimeout(() => {
-          this.delayed = false
-        }, delay)
-      }
-    }
-
-    render(props: any, slots: Slots) {
-      if (this.err || (this.timedOut && !this.comp)) {
-        const error =
-          this.err || new Error(`Async component timed out after ${timeout}ms.`)
-        return errorComp
-          ? createComponentVNode(
-              errorComp,
-              { error },
-              null,
-              ChildrenFlags.NO_CHILDREN
-            )
-          : null
-      } else if (this.comp) {
-        return createComponentVNode(
-          this.comp,
-          unwrap(props),
-          slots,
-          ChildrenFlags.STABLE_SLOTS
-        )
-      } else {
-        return loadingComp && !this.delayed
-          ? createComponentVNode(
-              loadingComp,
-              null,
-              null,
-              ChildrenFlags.NO_CHILDREN
-            )
-          : null
-      }
-    }
-  } as ComponentClass
-}
diff --git a/packages/runtime-core/src/optional/await.ts b/packages/runtime-core/src/optional/await.ts
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/packages/runtime-core/src/optional/directives.ts b/packages/runtime-core/src/optional/directives.ts
deleted file mode 100644 (file)
index ba09cee..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
-Runtime helper for applying directives to a vnode. Example usage:
-
-const comp = resolveComponent(this, 'comp')
-const foo = resolveDirective(this, 'foo')
-const bar = resolveDirective(this, 'bar')
-
-return applyDirectives(
-  h(comp),
-  this,
-  [foo, this.x],
-  [bar, this.y]
-)
-*/
-
-import { VNode, cloneVNode, VNodeData } from '../vdom'
-import { ComponentInstance } from '../component'
-import { EMPTY_OBJ } from '@vue/shared'
-
-interface DirectiveBinding {
-  instance: ComponentInstance
-  value?: any
-  oldValue?: any
-  arg?: string
-  modifiers?: DirectiveModifiers
-}
-
-type DirectiveHook = (
-  el: any,
-  binding: DirectiveBinding,
-  vnode: VNode,
-  prevVNode: VNode | void
-) => void
-
-interface Directive {
-  beforeMount: DirectiveHook
-  mounted: DirectiveHook
-  beforeUpdate: DirectiveHook
-  updated: DirectiveHook
-  beforeUnmount: DirectiveHook
-  unmounted: DirectiveHook
-}
-
-type DirectiveModifiers = Record<string, boolean>
-
-const valueCache = new WeakMap<Directive, WeakMap<any, any>>()
-
-export function applyDirective(
-  data: VNodeData,
-  instance: ComponentInstance,
-  directive: Directive,
-  value?: any,
-  arg?: string,
-  modifiers?: DirectiveModifiers
-) {
-  let valueCacheForDir = valueCache.get(directive) as WeakMap<VNode, any>
-  if (!valueCacheForDir) {
-    valueCacheForDir = new WeakMap<VNode, any>()
-    valueCache.set(directive, valueCacheForDir)
-  }
-  for (const key in directive) {
-    const hook = directive[key as keyof Directive]
-    const hookKey = `vnode` + key[0].toUpperCase() + key.slice(1)
-    const vnodeHook = (vnode: VNode, prevVNode?: VNode) => {
-      let oldValue
-      if (prevVNode !== void 0) {
-        oldValue = valueCacheForDir.get(prevVNode)
-        valueCacheForDir.delete(prevVNode)
-      }
-      valueCacheForDir.set(vnode, value)
-      hook(
-        vnode.el,
-        {
-          instance,
-          value,
-          oldValue,
-          arg,
-          modifiers
-        },
-        vnode,
-        prevVNode
-      )
-    }
-    const existing = data[hookKey]
-    data[hookKey] = existing
-      ? [].concat(existing as any, vnodeHook as any)
-      : vnodeHook
-  }
-}
-
-type DirectiveArguments = [
-  Directive,
-  any,
-  string | undefined,
-  DirectiveModifiers | undefined
-][]
-
-export function applyDirectives(
-  vnode: VNode,
-  instance: ComponentInstance,
-  ...directives: DirectiveArguments
-) {
-  vnode = cloneVNode(vnode, EMPTY_OBJ)
-  for (let i = 0; i < directives.length; i++) {
-    applyDirective(vnode.data as VNodeData, instance, ...directives[i])
-  }
-  return vnode
-}
diff --git a/packages/runtime-core/src/optional/eventEmitter.ts b/packages/runtime-core/src/optional/eventEmitter.ts
deleted file mode 100644 (file)
index 2fba5fc..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-import { isArray } from '@vue/shared'
-
-export class EventEmitter {
-  ctx: any
-  events: { [event: string]: Function[] | null } = {}
-
-  constructor(ctx: any) {
-    this.ctx = ctx
-  }
-
-  // eventEmitter interface
-  on(event: string, fn: Function) {
-    if (isArray(event)) {
-      for (let i = 0; i < event.length; i++) {
-        this.on(event[i], fn)
-      }
-    } else {
-      const { events } = this
-      ;(events[event] || (events[event] = [])).push(fn)
-    }
-  }
-
-  once(event: string, fn: Function) {
-    const onceFn = (...args: any[]) => {
-      this.off(event, onceFn)
-      fn.apply(this, args)
-    }
-    ;(onceFn as any).fn = fn
-    this.on(event, onceFn)
-  }
-
-  off(event?: string, fn?: Function) {
-    if (!event && !fn) {
-      this.events = {}
-    } else if (isArray(event)) {
-      for (let i = 0; i < event.length; i++) {
-        this.off(event[i], fn)
-      }
-    } else if (!fn) {
-      this.events[event as string] = null
-    } else {
-      const fns = this.events[event as string]
-      if (fns) {
-        for (let i = 0; i < fns.length; i++) {
-          const f = fns[i]
-          if (fn === f || fn === (f as any).fn) {
-            fns.splice(i, 1)
-            break
-          }
-        }
-      }
-    }
-  }
-
-  emit(name: string, ...payload: any[]) {
-    const handlers = this.events[name]
-    if (handlers) {
-      invokeListeners(handlers, payload, this.ctx)
-    }
-  }
-}
-
-export function invokeListeners(
-  value: Function | Function[],
-  payload: any[],
-  ctx: any = null
-) {
-  // TODO handle error
-  if (isArray(value)) {
-    for (let i = 0; i < value.length; i++) {
-      value[i].call(ctx, ...payload)
-    }
-  } else {
-    value.call(ctx, ...payload)
-  }
-}
diff --git a/packages/runtime-core/src/optional/keepAlive.ts b/packages/runtime-core/src/optional/keepAlive.ts
deleted file mode 100644 (file)
index 8359588..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-import { Component, ComponentClass, ComponentInstance } from '../component'
-import { VNode, Slots, cloneVNode } from '../vdom'
-import { VNodeFlags } from '../flags'
-import { warn } from '../warning'
-import { isString, isArray } from '@vue/shared'
-
-type MatchPattern = string | RegExp | string[] | RegExp[]
-
-interface KeepAliveProps {
-  include?: MatchPattern
-  exclude?: MatchPattern
-  max?: number | string
-}
-
-type CacheKey = string | number | ComponentClass
-type Cache = Map<CacheKey, VNode>
-
-export const KeepAliveSymbol = Symbol()
-
-export class KeepAlive extends Component<KeepAliveProps> {
-  private cache: Cache
-  private keys: Set<CacheKey>
-
-  created() {
-    this.cache = new Map()
-    this.keys = new Set()
-  }
-
-  // to be set in createRenderer when instance is created
-  $unmount: (instance: ComponentInstance) => void
-
-  beforeUnmount() {
-    this.cache.forEach(vnode => {
-      // change flag so it can be properly unmounted
-      vnode.flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
-      this.$unmount(vnode.children as ComponentInstance)
-    })
-  }
-
-  pruneCache(filter?: (name: string) => boolean) {
-    this.cache.forEach((vnode, key) => {
-      const name = getName(vnode.tag as ComponentClass)
-      if (name && (!filter || !filter(name))) {
-        this.pruneCacheEntry(key)
-      }
-    })
-  }
-
-  pruneCacheEntry(key: CacheKey) {
-    const cached = this.cache.get(key) as VNode
-    const current = this.$vnode
-    if (!current || cached.tag !== current.tag) {
-      this.$unmount(cached.children as ComponentInstance)
-    }
-    this.cache.delete(key)
-    this.keys.delete(key)
-  }
-
-  render(props: KeepAliveProps, slots: Slots) {
-    if (!slots.default) {
-      return
-    }
-    const children = slots.default()
-    let vnode = children[0]
-    if (children.length > 1) {
-      if (__DEV__) {
-        warn(`KeepAlive can only have a single child.`)
-      }
-      return children
-    } else if ((vnode.flags & VNodeFlags.COMPONENT_STATEFUL) === 0) {
-      return children
-    }
-
-    const comp = vnode.tag as ComponentClass
-    const name = getName(comp)
-    const { include, exclude, max } = props
-
-    if (
-      (include && (!name || !matches(include, name))) ||
-      (exclude && name && matches(exclude, name))
-    ) {
-      return vnode
-    }
-
-    const { cache, keys } = this
-    const key = vnode.key == null ? comp : vnode.key
-    const cached = cache.get(key)
-
-    // clone vnode if it's reused because we are going to mutate its flags
-    if (vnode.el) {
-      vnode = cloneVNode(vnode)
-    }
-    cache.set(key, vnode)
-
-    if (cached) {
-      vnode.children = cached.children
-      // avoid vnode being mounted as fresh
-      vnode.flags |= VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE
-      // make this key the freshest
-      keys.delete(key)
-      keys.add(key)
-    } else {
-      keys.add(key)
-      // prune oldest entry
-      if (max && keys.size > parseInt(max as string, 10)) {
-        this.pruneCacheEntry(Array.from(this.keys)[0])
-      }
-    }
-    // avoid vnode being unmounted
-    vnode.flags |= VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE
-    return vnode
-  }
-}
-
-// mark constructor
-// we use a symbol instead of comparing to the constructor itself
-// so that the implementation can be tree-shaken
-;(KeepAlive as any)[KeepAliveSymbol] = true
-
-function getName(comp: ComponentClass): string | void {
-  return comp.displayName || comp.name
-}
-
-function matches(pattern: MatchPattern, name: string): boolean {
-  if (isArray(pattern)) {
-    return (pattern as any).some((p: string | RegExp) => matches(p, name))
-  } else if (isString(pattern)) {
-    return pattern.split(',').indexOf(name) > -1
-  } else if (pattern.test) {
-    return pattern.test(name)
-  }
-  /* istanbul ignore next */
-  return false
-}
diff --git a/packages/runtime-core/src/optional/memoize.ts b/packages/runtime-core/src/optional/memoize.ts
deleted file mode 100644 (file)
index d1a77b6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-// Used for memoizing trees inside render functions.
-//
-// Example (equivalent of v-once):
-//
-//   render() {
-//     return memoize(h('div', this.msg), this, 0)
-//   }
-//
-// Memoize baesd on keys:
-//
-//   render() {
-//     return memoize(h('div', this.msg + this.count), this, 0, [this.msg])
-//   }
-
-// TODO how does this work in v-for?
-// probably need to take vnode key into consideration
-
-import { Component } from '../component'
-import { warn } from '../warning'
-
-const memoizeMap = new WeakMap()
-
-export function memoize<T>(
-  getter: () => T,
-  instance: Component,
-  id: number,
-  keys?: any[]
-): T {
-  if (__DEV__ && arguments.length > 3 && !Array.isArray(keys)) {
-    warn(
-      `keys passed to v-memo or memoize must be an array. Got ${String(keys)}`
-    )
-  }
-  let storage = memoizeMap.get(instance)
-  if (!storage) {
-    storage = []
-    memoizeMap.set(instance, storage)
-  }
-  const record = storage[id]
-  if (!record) {
-    const value = getter()
-    storage[id] = [value, keys]
-    return value
-  } else {
-    const [prevValue, prevKeys] = record
-    record[1] = keys
-    if (keys) {
-      for (let i = 0; i < keys.length; i++) {
-        if (keys[i] !== prevKeys[i]) {
-          return (record[0] = getter())
-        }
-      }
-    }
-    return prevValue
-  }
-}
diff --git a/packages/runtime-core/src/optional/mixins.ts b/packages/runtime-core/src/optional/mixins.ts
deleted file mode 100644 (file)
index 86eb6cb..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Component } from '../component'
-import { createComponentClassFromOptions } from '../componentOptions'
-import {
-  ComponentOptions,
-  resolveComponentOptionsFromClass,
-  mergeComponentOptions,
-  mergeDataFn
-} from '../componentOptions'
-import { normalizePropsOptions } from '../componentProps'
-import { extractInitializers } from '../componentState'
-import { isFunction } from '@vue/shared'
-
-interface ComponentConstructor<This = Component> {
-  new (): This
-}
-
-interface ComponentConstructorWithMixins<This> {
-  new <P = {}, D = {}>(): This & { $data: D } & D & { $props: Readonly<P> } & P
-}
-
-// mind = blown
-// https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type
-type UnionToIntersection<U> = (U extends any
-  ? (k: U) => void
-  : never) extends ((k: infer I) => void)
-  ? I
-  : never
-
-type ExtractInstance<T> = T extends (infer U)[]
-  ? UnionToIntersection<U extends ComponentConstructor<infer V> ? V : never>
-  : never
-
-export function mixins<
-  T extends ComponentConstructor[] = [],
-  V = ExtractInstance<T>
->(...args: T): ComponentConstructorWithMixins<V>
-export function mixins(...args: any[]): any {
-  let options: ComponentOptions = {}
-  args.forEach(mixin => {
-    if (isFunction(mixin)) {
-      const Class = mixin
-      mixin = resolveComponentOptionsFromClass(Class)
-      // in order to extract properties initialized in the mixin's constructor,
-      // we create an instance of it and pass in the actual props - this
-      // short-circuits the normal component initialization and allows us to
-      // relatively-cheaply extract the properties added in the constructor.
-      function extractData() {
-        return extractInitializers(new Class(this.$props))
-      }
-      const { data } = mixin
-      mixin.data = data ? mergeDataFn(data, extractData) : extractData
-    } else {
-      mixin.props = normalizePropsOptions(mixin.props)
-    }
-    options = mergeComponentOptions(options, mixin)
-  })
-  return createComponentClassFromOptions(options)
-}
-
-/* Example usage
-
-class Foo extends Component<{ foo: number }> {
-  test() {
-
-  }
-}
-
-class Bar extends Component<{ bar: string }> {
-  ok() {
-
-  }
-}
-
-class Baz extends mixins(Foo, Bar)<{ baz: number }> {
-  created() {
-    this.foo
-    this.bar
-    this.baz
-    this.test()
-    this.ok()
-  }
-}
-
-*/
diff --git a/packages/runtime-core/src/patchFlags.ts b/packages/runtime-core/src/patchFlags.ts
new file mode 100644 (file)
index 0000000..5d6a0b7
--- /dev/null
@@ -0,0 +1,6 @@
+export const TEXT = 1
+export const CLASS = 1 << 1
+export const STYLE = 1 << 2
+export const PROPS = 1 << 3
+export const KEYED = 1 << 4
+export const UNKEYED = 1 << 5
diff --git a/packages/runtime-core/src/vdom.ts b/packages/runtime-core/src/vdom.ts
deleted file mode 100644 (file)
index f0fd228..0000000
+++ /dev/null
@@ -1,449 +0,0 @@
-import {
-  ComponentInstance,
-  ComponentClass,
-  FunctionalComponent
-} from './component'
-import { VNodeFlags, ChildrenFlags } from './flags'
-import { createComponentClassFromOptions } from './componentOptions'
-import { EMPTY_OBJ, isObject, isArray, isFunction, isString } from '@vue/shared'
-import { RawChildrenType, RawSlots } from './h'
-import { FunctionalHandle } from './createRenderer'
-
-const handlersRE = /^on|^vnode/
-const STABLE_SLOTS_HINT = '$stable'
-
-// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
-export interface RenderNode {
-  vnode?: VNode | null
-  // technically this doesn't exist on platform render nodes,
-  // but we list it here so that TS can figure out union types
-  $f: false
-}
-
-export interface VNode {
-  _isVNode: true
-  flags: VNodeFlags
-  tag: string | FunctionalComponent | ComponentClass | RenderNode | null
-  data: VNodeData | null
-  children: VNodeChildren
-  childFlags: ChildrenFlags
-  key: Key | null
-  ref: Ref | null
-  slots: Slots | null
-  // only on mounted nodes
-  el: RenderNode | null
-  // only on mounted component nodes that is also a root node (HOCs)
-  // points to parent component's placeholder vnode
-  // this is used to update vnode.el for nested HOCs.
-  parentVNode: VNode | null
-  // only on mounted component nodes
-  // points to the parent stateful/functional component's placeholder node
-  contextVNode: VNode | null
-  // only on mounted functional component nodes
-  // a consistent handle so that a functional component can be identified
-  // by the scheduler
-  handle: FunctionalHandle | null
-  // only on cloned vnodes, points to the original cloned vnode
-  clonedFrom: VNode | null
-}
-
-export interface MountedVNode extends VNode {
-  el: RenderNode
-}
-
-export interface BuiltInProps {
-  key?: Key | null
-  ref?: Ref | null
-  slots?: RawSlots | null
-}
-
-export type VNodeData = {
-  [key: string]: any
-} & BuiltInProps
-
-export type VNodeChildren =
-  | VNode[] // ELEMENT | PORTAL
-  | ComponentInstance // COMPONENT_STATEFUL
-  | VNode // COMPONENT_FUNCTIONAL
-  | string // TEXT
-  | null
-
-export type Key = string | number
-
-export type Ref = (t: RenderNode | ComponentInstance | null) => void
-
-export type Slot = (...args: any[]) => VNode[]
-
-export type Slots = Readonly<{
-  [name: string]: Slot
-}>
-
-export function createVNode(
-  flags: VNodeFlags,
-  tag: string | FunctionalComponent | ComponentClass | RenderNode | null,
-  data: VNodeData | null,
-  children: RawChildrenType | null,
-  childFlags: ChildrenFlags,
-  key: Key | null | undefined,
-  ref: Ref | null | undefined,
-  slots: Slots | null | undefined
-): VNode {
-  const vnode: VNode = {
-    _isVNode: true,
-    flags,
-    tag,
-    data,
-    children: children as VNodeChildren,
-    childFlags,
-    key: key === void 0 ? null : key,
-    ref: ref === void 0 ? null : ref,
-    slots: slots === void 0 ? null : slots,
-    el: null,
-    parentVNode: null,
-    contextVNode: null,
-    handle: null,
-    clonedFrom: null
-  }
-  if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
-    normalizeChildren(vnode, children)
-  }
-  return vnode
-}
-
-export function createElementVNode(
-  tag: string,
-  data: VNodeData | null,
-  children: RawChildrenType | null,
-  childFlags: ChildrenFlags,
-  key?: Key | null,
-  ref?: Ref | null
-) {
-  const flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
-  if (data !== null) {
-    normalizeClassAndStyle(data)
-  }
-  return createVNode(flags, tag, data, children, childFlags, key, ref, null)
-}
-
-function normalizeClassAndStyle(data: VNodeData) {
-  if (data.class != null) {
-    data.class = normalizeClass(data.class)
-  }
-  if (data.style != null) {
-    data.style = normalizeStyle(data.style)
-  }
-}
-
-function normalizeStyle(value: any): Record<string, string | number> | void {
-  if (isArray(value)) {
-    const res: Record<string, string | number> = {}
-    for (let i = 0; i < value.length; i++) {
-      const normalized = normalizeStyle(value[i])
-      if (normalized) {
-        for (const key in normalized) {
-          res[key] = normalized[key]
-        }
-      }
-    }
-    return res
-  } else if (isObject(value)) {
-    return value
-  }
-}
-
-function normalizeClass(value: any): string {
-  let res = ''
-  if (isString(value)) {
-    res = value
-  } else if (isArray(value)) {
-    for (let i = 0; i < value.length; i++) {
-      res += normalizeClass(value[i]) + ' '
-    }
-  } else if (isObject(value)) {
-    for (const name in value) {
-      if (value[name]) {
-        res += name + ' '
-      }
-    }
-  }
-  return res.trim()
-}
-
-export function createComponentVNode(
-  comp: any,
-  data: VNodeData | null,
-  children: RawChildrenType | Slots,
-  childFlags: ChildrenFlags,
-  key?: Key | null,
-  ref?: Ref | null
-) {
-  // resolve type
-  let flags: VNodeFlags
-
-  // flags
-  if (isObject(comp)) {
-    if (comp.functional) {
-      // object literal functional
-      flags = VNodeFlags.COMPONENT_FUNCTIONAL
-      const { render } = comp
-      if (!comp._normalized) {
-        render.pure = comp.pure
-        render.props = comp.props
-        comp._normalized = true
-      }
-      comp = render
-    } else {
-      // object literal stateful
-      flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
-      comp =
-        comp._normalized ||
-        (comp._normalized = createComponentClassFromOptions(comp))
-    }
-  } else {
-    // assumes comp is function here now
-    if (__DEV__ && !isFunction(comp)) {
-      // TODO warn invalid comp value in dev
-    }
-    if (comp.prototype && comp.prototype.render) {
-      flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
-    } else {
-      flags = VNodeFlags.COMPONENT_FUNCTIONAL
-    }
-  }
-
-  if (__DEV__ && flags === VNodeFlags.COMPONENT_FUNCTIONAL && ref) {
-    // TODO warn functional component cannot have ref
-  }
-
-  // slots
-  let slots: any
-  if (childFlags === ChildrenFlags.STABLE_SLOTS) {
-    slots = children
-  } else if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
-    childFlags = children
-      ? ChildrenFlags.DYNAMIC_SLOTS
-      : ChildrenFlags.NO_CHILDREN
-    if (children != null) {
-      if (isFunction(children)) {
-        // function as children
-        slots = { default: children }
-      } else if (isObject(children) && !(children as any)._isVNode) {
-        // slot object as children
-        slots = children
-        // special manual optimization hint for raw render fn users
-        if (slots[STABLE_SLOTS_HINT]) {
-          childFlags = ChildrenFlags.STABLE_SLOTS
-        }
-      } else {
-        slots = { default: () => children }
-      }
-      slots = normalizeSlots(slots)
-    }
-  }
-
-  // class & style
-  if (data !== null) {
-    normalizeClassAndStyle(data)
-  }
-
-  return createVNode(
-    flags,
-    comp,
-    data,
-    null, // to be set during mount
-    childFlags,
-    key,
-    ref,
-    slots
-  )
-}
-
-export function createTextVNode(text: string): VNode {
-  return createVNode(
-    VNodeFlags.TEXT,
-    null,
-    null,
-    text == null ? '' : text,
-    ChildrenFlags.NO_CHILDREN,
-    null,
-    null,
-    null
-  )
-}
-
-export function createFragment(
-  children: RawChildrenType,
-  childFlags?: ChildrenFlags,
-  key?: Key | null
-) {
-  return createVNode(
-    VNodeFlags.FRAGMENT,
-    null,
-    null,
-    children,
-    childFlags === void 0 ? ChildrenFlags.UNKNOWN_CHILDREN : childFlags,
-    key,
-    null,
-    null
-  )
-}
-
-export function createPortal(
-  target: RenderNode | string,
-  children: RawChildrenType,
-  childFlags?: ChildrenFlags,
-  key?: Key | null,
-  ref?: Ref | null
-): VNode {
-  return createVNode(
-    VNodeFlags.PORTAL,
-    target,
-    null,
-    children,
-    childFlags === void 0 ? ChildrenFlags.UNKNOWN_CHILDREN : childFlags,
-    key,
-    ref,
-    null
-  )
-}
-
-export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
-  const { flags, data } = vnode
-  if (flags & VNodeFlags.ELEMENT || flags & VNodeFlags.COMPONENT) {
-    let clonedData = data
-    if (extraData != null) {
-      clonedData = {}
-      if (data != null) {
-        for (const key in data) {
-          clonedData[key] = data[key]
-        }
-      }
-      if (extraData !== EMPTY_OBJ) {
-        for (const key in extraData) {
-          if (key === 'class') {
-            clonedData.class = normalizeClass([
-              clonedData.class,
-              extraData.class
-            ])
-          } else if (key === 'style') {
-            clonedData.style = normalizeStyle([
-              clonedData.style,
-              extraData.style
-            ])
-          } else if (handlersRE.test(key)) {
-            // on*, vnode*
-            const existing = clonedData[key]
-            clonedData[key] = existing
-              ? [].concat(existing, extraData[key])
-              : extraData[key]
-          } else {
-            clonedData[key] = extraData[key]
-          }
-        }
-      }
-    }
-    const cloned = createVNode(
-      flags,
-      vnode.tag,
-      clonedData,
-      vnode.children as RawChildrenType,
-      vnode.childFlags,
-      vnode.key,
-      vnode.ref,
-      vnode.slots
-    )
-    cloned.clonedFrom = vnode.clonedFrom || vnode
-    return cloned
-  } else if (flags & VNodeFlags.TEXT) {
-    return createTextVNode(vnode.children as string)
-  } else {
-    return vnode
-  }
-}
-
-function normalizeChildren(vnode: VNode, children: any) {
-  let childFlags
-  if (isArray(children)) {
-    const { length } = children
-    if (length === 0) {
-      childFlags = ChildrenFlags.NO_CHILDREN
-      children = null
-    } else if (length === 1) {
-      childFlags = ChildrenFlags.SINGLE_VNODE
-      children = children[0]
-      if (children.el) {
-        children = cloneVNode(children)
-      }
-    } else {
-      childFlags = ChildrenFlags.KEYED_VNODES
-      children = normalizeVNodes(children)
-    }
-  } else if (children == null) {
-    childFlags = ChildrenFlags.NO_CHILDREN
-  } else if (children._isVNode) {
-    childFlags = ChildrenFlags.SINGLE_VNODE
-    if (children.el) {
-      children = cloneVNode(children)
-    }
-  } else {
-    // primitives or invalid values, cast to string
-    childFlags = ChildrenFlags.SINGLE_VNODE
-    children = createTextVNode(children + '')
-  }
-  vnode.children = children
-  vnode.childFlags = childFlags
-}
-
-export function normalizeVNodes(
-  children: any[],
-  newChildren: VNode[] = [],
-  currentPrefix: string = ''
-): VNode[] {
-  for (let i = 0; i < children.length; i++) {
-    const child = children[i]
-    let newChild
-    if (child == null) {
-      newChild = createTextVNode('')
-    } else if (child._isVNode) {
-      newChild = child.el ? cloneVNode(child) : child
-    } else if (isArray(child)) {
-      normalizeVNodes(child, newChildren, currentPrefix + i + '|')
-    } else {
-      newChild = createTextVNode(child + '')
-    }
-    if (newChild) {
-      if (newChild.key == null) {
-        newChild.key = currentPrefix + i
-      }
-      newChildren.push(newChild)
-    }
-  }
-  return newChildren
-}
-
-// ensure all slot functions return Arrays
-function normalizeSlots(slots: { [name: string]: any }): Slots {
-  if (slots._normalized) {
-    return slots
-  }
-  const normalized = { _normalized: true } as any
-  for (const name in slots) {
-    if (name === STABLE_SLOTS_HINT) {
-      continue
-    }
-    normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
-  }
-  return normalized
-}
-
-function normalizeSlot(value: any): VNode[] {
-  if (value == null) {
-    return [createTextVNode('')]
-  } else if (isArray(value)) {
-    return normalizeVNodes(value)
-  } else if (value._isVNode) {
-    return [value]
-  } else {
-    return [createTextVNode(value + '')]
-  }
-}
diff --git a/packages/runtime-core/src/warning.ts b/packages/runtime-core/src/warning.ts
deleted file mode 100644 (file)
index 4bc22a0..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-import { ComponentType, ComponentClass, FunctionalComponent } from './component'
-import { EMPTY_OBJ, isString } from '@vue/shared'
-import { VNode } from './vdom'
-import { Data } from './componentOptions'
-
-let stack: VNode[] = []
-
-type TraceEntry = {
-  type: VNode
-  recurseCount: number
-}
-
-type ComponentTraceStack = TraceEntry[]
-
-export function pushWarningContext(vnode: VNode) {
-  stack.push(vnode)
-}
-
-export function popWarningContext() {
-  stack.pop()
-}
-
-export function warn(msg: string, ...args: any[]) {
-  // TODO warn handler
-  console.warn(`[Vue warn]: ${msg}`, ...args)
-  const trace = getComponentTrace()
-  if (!trace.length) {
-    return
-  }
-  if (console.groupCollapsed) {
-    console.groupCollapsed('at', ...formatTraceEntry(trace[0]))
-    const logs: string[] = []
-    trace.slice(1).forEach((entry, i) => {
-      if (i !== 0) logs.push('\n')
-      logs.push(...formatTraceEntry(entry, i + 1))
-    })
-    console.log(...logs)
-    console.groupEnd()
-  } else {
-    const logs: string[] = []
-    trace.forEach((entry, i) => {
-      const formatted = formatTraceEntry(entry, i)
-      if (i === 0) {
-        logs.push('at', ...formatted)
-      } else {
-        logs.push('\n', ...formatted)
-      }
-    })
-    console.log(...logs)
-  }
-}
-
-function getComponentTrace(): ComponentTraceStack {
-  let current: VNode | null | undefined = stack[stack.length - 1]
-  if (!current) {
-    return []
-  }
-
-  // we can't just use the stack because it will be incomplete during updates
-  // that did not start from the root. Re-construct the parent chain using
-  // contextVNode information.
-  const normlaizedStack: ComponentTraceStack = []
-
-  while (current) {
-    const last = normlaizedStack[0]
-    if (last && last.type === current) {
-      last.recurseCount++
-    } else {
-      normlaizedStack.push({
-        type: current,
-        recurseCount: 0
-      })
-    }
-    current = current.contextVNode
-  }
-
-  return normlaizedStack
-}
-
-function formatTraceEntry(
-  { type, recurseCount }: TraceEntry,
-  depth: number = 0
-): string[] {
-  const padding = depth === 0 ? '' : ' '.repeat(depth * 2 + 1)
-  const postfix =
-    recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
-  const open = padding + `<${formatComponentName(type.tag as ComponentType)}`
-  const close = `>` + postfix
-  return type.data ? [open, ...formatProps(type.data), close] : [open + close]
-}
-
-const classifyRE = /(?:^|[-_])(\w)/g
-const classify = (str: string): string =>
-  str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
-
-function formatComponentName(c: ComponentType, file?: string): string {
-  let name: string
-
-  if (c.prototype && c.prototype.render) {
-    // stateful
-    const cc = c as ComponentClass
-    const options = cc.options || EMPTY_OBJ
-    name = options.displayName || cc.name
-  } else {
-    // functional
-    const fc = c as FunctionalComponent
-    name = fc.displayName || fc.name
-  }
-
-  if (file && name === 'AnonymousComponent') {
-    const match = file.match(/([^/\\]+)\.vue$/)
-    if (match) {
-      name = match[1]
-    }
-  }
-
-  return classify(name)
-}
-
-function formatProps(props: Data) {
-  const res = []
-  for (const key in props) {
-    const value = props[key]
-    if (isString(value)) {
-      res.push(`${key}=${JSON.stringify(value)}`)
-    } else {
-      res.push(`${key}=`, value)
-    }
-  }
-  return res
-}