import { createRenderer, VNode } from '@vue/runtime-core'
-import { DOMRendererOptions } from './rendererOptions'
+import { nodeOps } from './nodeOps'
+import { patchProp } from './patchProp'
-export const render = createRenderer(DOMRendererOptions) as (
- vnode: VNode | null,
- container: HTMLElement
-) => VNode
+export const render = createRenderer({
+ patchProp,
+ ...nodeOps
+}) as (vnode: VNode | null, container: HTMLElement) => VNode
// re-export everything from core
// h, Component, observer API, nextTick, flags & types
-import { RendererOptions } from '@vue/runtime-core'
-import { patchProp } from './patchProp'
-
const doc = document
const svgNS = 'http://www.w3.org/2000/svg'
-export const DOMRendererOptions: RendererOptions = {
- patchProp,
-
+export const nodeOps = {
insert: (child: Node, parent: Node, anchor?: Node) => {
if (anchor != null) {
parent.insertBefore(child, anchor)
},
remove: (child: Node) => {
- if (!child) debugger
const parent = child.parentNode
if (parent != null) {
parent.removeChild(child)
import {
- h,
+ createVNode as h,
render,
- Component,
nodeOps,
NodeTypes,
TestElement,
- TestText,
- dumpOps,
- NodeOpTypes,
- nextTick,
- observable,
- resetOps,
- serialize,
- renderInstance,
- triggerEvent
+ TestText
+ // dumpOps,
+ // NodeOpTypes,
+ // nextTick,
+ // state,
+ // resetOps,
+ // serialize,
+ // triggerEvent
} from '../src'
describe('test renderer', () => {
it('should work', async () => {
- class App extends Component {
- render() {
- return h(
- 'div',
- {
- id: 'test'
- },
- 'hello'
- )
- }
- }
const root = nodeOps.createElement('div')
- await render(h(App), root)
+ render(
+ h(
+ 'div',
+ {
+ id: 'test'
+ },
+ 'hello'
+ ),
+ root
+ )
expect(root.children.length).toBe(1)
expect(text.text).toBe('hello')
})
- it('should record ops', async () => {
- const state = observable({
- id: 'test',
- text: 'hello'
- })
-
- class App extends Component {
- render() {
- return h(
- 'div',
- {
- id: state.id
- },
- state.text
- )
- }
- }
- const root = nodeOps.createElement('div')
-
- resetOps()
- await render(h(App), root)
- const ops = dumpOps()
-
- expect(ops.length).toBe(5)
-
- expect(ops[0]).toEqual({
- type: NodeOpTypes.CREATE,
- nodeType: NodeTypes.ELEMENT,
- tag: 'div',
- targetNode: root.children[0]
- })
-
- expect(ops[1]).toEqual({
- type: NodeOpTypes.PATCH,
- targetNode: root.children[0],
- propKey: 'id',
- propPrevValue: null,
- propNextValue: 'test'
- })
-
- expect(ops[2]).toEqual({
- type: NodeOpTypes.CREATE,
- nodeType: NodeTypes.TEXT,
- text: 'hello',
- targetNode: (root.children[0] as TestElement).children[0]
- })
-
- expect(ops[3]).toEqual({
- type: NodeOpTypes.APPEND,
- targetNode: (root.children[0] as TestElement).children[0],
- parentNode: root.children[0]
- })
-
- expect(ops[4]).toEqual({
- type: NodeOpTypes.APPEND,
- targetNode: root.children[0],
- parentNode: root
- })
-
- // test update ops
- state.id = 'foo'
- state.text = 'bar'
- await nextTick()
-
- const updateOps = dumpOps()
- expect(updateOps.length).toBe(2)
-
- expect(updateOps[0]).toEqual({
- type: NodeOpTypes.PATCH,
- targetNode: root.children[0],
- propKey: 'id',
- propPrevValue: 'test',
- propNextValue: 'foo'
- })
-
- expect(updateOps[1]).toEqual({
- type: NodeOpTypes.SET_TEXT,
- targetNode: (root.children[0] as TestElement).children[0],
- text: 'bar'
- })
- })
-
- it('should be able to serialize nodes', async () => {
- class App extends Component {
- render() {
- return h(
- 'div',
- {
- id: 'test'
- },
- [h('span', 'foo'), 'hello']
- )
- }
- }
- const root = nodeOps.createElement('div')
- await render(h(App), root)
- expect(serialize(root)).toEqual(
- `<div><div id="test"><span>foo</span>hello</div></div>`
- )
- expect(serialize(root, 2)).toEqual(
- `<div>
- <div id="test">
- <span>
- foo
- </span>
- hello
- </div>
-</div>`
- )
- })
-
- it('should be able to trigger events', async () => {
- class App extends Component {
- count = 0
- inc() {
- this.count++
- }
- render() {
- return h(
- 'div',
- {
- onClick: this.inc
- },
- this.count
- )
- }
- }
- const app = await renderInstance(App)
- triggerEvent(app.$el, 'click')
- expect(app.count).toBe(1)
- await nextTick()
- expect(serialize(app.$el)).toBe(`<div>1</div>`)
- })
+ // it('should record ops', async () => {
+ // const store = state({
+ // id: 'test',
+ // text: 'hello'
+ // })
+
+ // class App extends Component {
+ // render() {
+ // return h(
+ // 'div',
+ // {
+ // id: store.id
+ // },
+ // store.text
+ // )
+ // }
+ // }
+ // const root = nodeOps.createElement('div')
+
+ // resetOps()
+ // await render(h(App), root)
+ // const ops = dumpOps()
+
+ // expect(ops.length).toBe(5)
+
+ // expect(ops[0]).toEqual({
+ // type: NodeOpTypes.CREATE,
+ // nodeType: NodeTypes.ELEMENT,
+ // tag: 'div',
+ // targetNode: root.children[0]
+ // })
+
+ // expect(ops[1]).toEqual({
+ // type: NodeOpTypes.PATCH,
+ // targetNode: root.children[0],
+ // propKey: 'id',
+ // propPrevValue: null,
+ // propNextValue: 'test'
+ // })
+
+ // expect(ops[2]).toEqual({
+ // type: NodeOpTypes.CREATE,
+ // nodeType: NodeTypes.TEXT,
+ // text: 'hello',
+ // targetNode: (root.children[0] as TestElement).children[0]
+ // })
+
+ // expect(ops[3]).toEqual({
+ // type: NodeOpTypes.APPEND,
+ // targetNode: (root.children[0] as TestElement).children[0],
+ // parentNode: root.children[0]
+ // })
+
+ // expect(ops[4]).toEqual({
+ // type: NodeOpTypes.APPEND,
+ // targetNode: root.children[0],
+ // parentNode: root
+ // })
+
+ // // test update ops
+ // store.id = 'foo'
+ // store.text = 'bar'
+ // await nextTick()
+
+ // const updateOps = dumpOps()
+ // expect(updateOps.length).toBe(2)
+
+ // expect(updateOps[0]).toEqual({
+ // type: NodeOpTypes.PATCH,
+ // targetNode: root.children[0],
+ // propKey: 'id',
+ // propPrevValue: 'test',
+ // propNextValue: 'foo'
+ // })
+
+ // expect(updateOps[1]).toEqual({
+ // type: NodeOpTypes.SET_TEXT,
+ // targetNode: (root.children[0] as TestElement).children[0],
+ // text: 'bar'
+ // })
+ // })
+
+ // it('should be able to serialize nodes', async () => {
+ // class App extends Component {
+ // render() {
+ // return h(
+ // 'div',
+ // {
+ // id: 'test'
+ // },
+ // [h('span', 'foo'), 'hello']
+ // )
+ // }
+ // }
+ // const root = nodeOps.createElement('div')
+ // await render(h(App), root)
+ // expect(serialize(root)).toEqual(
+ // `<div><div id="test"><span>foo</span>hello</div></div>`
+ // )
+ // expect(serialize(root, 2)).toEqual(
+ // `<div>
+ // <div id="test">
+ // <span>
+ // foo
+ // </span>
+ // hello
+ // </div>
+ // </div>`
+ // )
+ // })
+
+ // it('should be able to trigger events', async () => {
+ // class App extends Component {
+ // count = 0
+ // inc() {
+ // this.count++
+ // }
+ // render() {
+ // return h(
+ // 'div',
+ // {
+ // onClick: this.inc
+ // },
+ // this.count
+ // )
+ // }
+ // }
+ // const app = await renderInstance(App)
+ // triggerEvent(app.$el, 'click')
+ // expect(app.count).toBe(1)
+ // await nextTick()
+ // expect(serialize(app.$el)).toBe(`<div>1</div>`)
+ // })
})
-import {
- h,
- createRenderer,
- Component,
- createComponentInstance
-} from '@vue/runtime-core'
+import { createRenderer, VNode } from '@vue/runtime-core'
import { nodeOps, TestElement } from './nodeOps'
-import { patchData } from './patchData'
+import { patchProp } from './patchProp'
-const { render: _render } = createRenderer({
- nodeOps,
- patchData
-})
-
-type publicRender = (
- node: {} | null,
- container: TestElement
-) => Promise<Component | null>
-export const render = _render as publicRender
-
-export function createInstance<T extends Component>(
- Class: new () => T,
- props?: any
-): T {
- return createComponentInstance(h(Class, props)).$proxy as any
-}
-
-export function renderInstance<T extends Component>(
- Class: new () => T,
- props?: any
-): Promise<T> {
- return render(h(Class, props), nodeOps.createElement('div')) as any
-}
+export const render = createRenderer({
+ patchProp,
+ ...nodeOps
+}) as (node: VNode | null, container: TestElement) => VNode
export { serialize } from './serialize'
export { triggerEvent } from './triggerEvent'
export const enum NodeTypes {
TEXT = 'text',
- ELEMENT = 'element'
+ ELEMENT = 'element',
+ COMMENT = 'comment'
}
export interface TestElement {
text: string
}
-export type TestNode = TestElement | TestText
+export interface TestComment {
+ id: number
+ type: NodeTypes.COMMENT
+ parentNode: TestElement | null
+ text: string
+}
+
+export type TestNode = TestElement | TestText | TestComment
export const enum NodeOpTypes {
CREATE = 'create',
INSERT = 'insert',
- APPEND = 'append',
REMOVE = 'remove',
SET_TEXT = 'setText',
- CLEAR = 'clearContent',
+ SET_ELEMENT_TEXT = 'setElementText',
PATCH = 'patch'
}
text?: string
targetNode?: TestNode
parentNode?: TestElement
- refNode?: TestNode
+ refNode?: TestNode | null
propKey?: string
propPrevValue?: any
propNextValue?: any
return node
}
-function setText(node: TestText, text: string) {
+function createComment(text: string): TestComment {
+ const node: TestComment = {
+ id: nodeId++,
+ type: NodeTypes.COMMENT,
+ text,
+ parentNode: null
+ }
logNodeOp({
- type: NodeOpTypes.SET_TEXT,
+ type: NodeOpTypes.CREATE,
+ nodeType: NodeTypes.COMMENT,
targetNode: node,
text
})
- node.text = text
+ return node
}
-function appendChild(parent: TestElement, child: TestNode) {
+function setText(node: TestText, text: string) {
logNodeOp({
- type: NodeOpTypes.APPEND,
- targetNode: child,
- parentNode: parent
+ type: NodeOpTypes.SET_TEXT,
+ targetNode: node,
+ text
})
- if (child.parentNode) {
- removeChild(child.parentNode, child)
- }
- parent.children.push(child)
- child.parentNode = parent
+ node.text = text
}
-function insertBefore(parent: TestElement, child: TestNode, ref: TestNode) {
- if (child.parentNode) {
- removeChild(child.parentNode, child)
- }
- const refIndex = parent.children.indexOf(ref)
- if (refIndex === -1) {
- console.error('ref: ', ref)
- console.error('parent: ', parent)
- throw new Error('ref is not a child of parent')
+function insert(child: TestNode, parent: TestElement, ref?: TestNode | null) {
+ let refIndex
+ if (ref != null) {
+ refIndex = parent.children.indexOf(ref)
+ if (refIndex === -1) {
+ console.error('ref: ', ref)
+ console.error('parent: ', parent)
+ throw new Error('ref is not a child of parent')
+ }
}
logNodeOp({
type: NodeOpTypes.INSERT,
parentNode: parent,
refNode: ref
})
- parent.children.splice(refIndex, 0, child)
- child.parentNode = parent
+ remove(child)
+ if (refIndex === undefined) {
+ parent.children.push(child)
+ child.parentNode = parent
+ } else {
+ parent.children.splice(refIndex, 0, child)
+ child.parentNode = parent
+ }
}
-function removeChild(parent: TestElement, child: TestNode) {
- logNodeOp({
- type: NodeOpTypes.REMOVE,
- targetNode: child,
- parentNode: parent
- })
- const i = parent.children.indexOf(child)
- if (i > -1) {
- parent.children.splice(i, 1)
- } else {
- console.error('target: ', child)
- console.error('parent: ', parent)
- throw Error('target is not a childNode of parent')
+function remove(child: TestNode) {
+ const parent = child.parentNode
+ if (parent != null) {
+ logNodeOp({
+ type: NodeOpTypes.REMOVE,
+ targetNode: child,
+ parentNode: parent
+ })
+ const i = parent.children.indexOf(child)
+ if (i > -1) {
+ parent.children.splice(i, 1)
+ } else {
+ console.error('target: ', child)
+ console.error('parent: ', parent)
+ throw Error('target is not a childNode of parent')
+ }
+ child.parentNode = null
}
- child.parentNode = null
}
-function clearContent(node: TestNode) {
+function setElementText(el: TestElement, text: string) {
logNodeOp({
- type: NodeOpTypes.CLEAR,
- targetNode: node
+ type: NodeOpTypes.SET_ELEMENT_TEXT,
+ targetNode: el,
+ text
})
- if (node.type === NodeTypes.ELEMENT) {
- node.children.forEach(c => {
- c.parentNode = null
- })
- node.children = []
- } else {
- node.text = ''
- }
+ el.children.forEach(c => {
+ c.parentNode = null
+ })
+ el.children = [createText(text)]
}
function parentNode(node: TestNode): TestElement | null {
}
export const nodeOps = {
+ insert,
+ remove,
createElement,
createText,
+ createComment,
setText,
- appendChild,
- insertBefore,
- removeChild,
- clearContent,
+ setElementText,
parentNode,
nextSibling,
querySelector
}
-
-export function patchData() {}
import { TestElement, logNodeOp, NodeOpTypes } from './nodeOps'
import { isOn } from '@vue/shared'
-export function patchData(
+export function patchProp(
el: TestElement,
key: string,
- prevValue: any,
- nextValue: any
+ nextValue: any,
+ prevValue: any
) {
logNodeOp({
type: NodeOpTypes.PATCH,
-import { TestElement, TestNode, NodeTypes, TestText } from './nodeOps'
+import {
+ TestElement,
+ TestNode,
+ NodeTypes,
+ TestText,
+ TestComment
+} from './nodeOps'
import { isOn } from '@vue/shared'
export function serialize(
)
}
-function serializeText(node: TestText, indent: number, depth: number): string {
+function serializeText(
+ node: TestText | TestComment,
+ indent: number,
+ depth: number
+): string {
const padding = indent ? ` `.repeat(indent).repeat(depth) : ``
- return padding + node.text
+ return (
+ padding +
+ (node.type === NodeTypes.COMMENT ? `<!--${node.text}-->` : node.text)
+ )
}