Skip to content

Commit 455a153

Browse files
committed
refactor: transformHArgs -> transformVNodeArgs
1 parent cba2f1a commit 455a153

File tree

5 files changed

+99
-101
lines changed

5 files changed

+99
-101
lines changed
Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { h, transformHArgs, resetTransformHArgs } from '../src/h'
1+
import { h } from '../src/h'
22
import { createVNode } from '../src/vnode'
3-
import { ComponentInternalInstance } from '@vue/runtime-core'
4-
import { createApp } from '@vue/runtime-dom'
53

64
// Since h is a thin layer on top of createVNode, we are only testing its
75
// own logic here. Details of vnode creation is tested in vnode.spec.ts.
8-
const testH = () => {
6+
describe('renderer: h', () => {
97
test('type only', () => {
108
expect(h('div')).toMatchObject(createVNode('div'))
119
})
@@ -59,62 +57,4 @@ const testH = () => {
5957
})
6058
)
6159
})
62-
}
63-
64-
describe('renderer: h', testH)
65-
66-
describe('renderer: transformHArgs', () => {
67-
describe('no-op pass-through', () => {
68-
beforeAll(() => {
69-
transformHArgs((hArgs: unknown[]) => hArgs)
70-
})
71-
72-
afterAll(resetTransformHArgs)
73-
74-
testH()
75-
})
76-
77-
describe('args is used directly, without merging', () => {
78-
beforeAll(() => {
79-
transformHArgs(() => ['h1', 'Hello World'])
80-
})
81-
82-
afterAll(resetTransformHArgs)
83-
84-
test('nodes become an h1 with text inside', () => {
85-
expect(h('div')).toMatchObject(createVNode('h1', null, 'Hello World'))
86-
})
87-
88-
test('resetting transformHArgs turns things back to normal', () => {
89-
expect(h('div')).toMatchObject(createVNode('h1', null, 'Hello World'))
90-
resetTransformHArgs()
91-
expect(h('div')).toMatchObject(createVNode('div'))
92-
})
93-
})
94-
95-
test('receives component instance as the 2nd arg', () => {
96-
transformHArgs((_: unknown[], instance: ComponentInternalInstance) => {
97-
return ['h1', instance.type.name] // <h1>{{ name }}</h1>
98-
})
99-
100-
const vm = createApp({
101-
// this will be the name of the component in the h1
102-
name: 'Root Component',
103-
render() {
104-
return h({
105-
// this code will never execute,
106-
// because it is overridden by the transformHArgs method
107-
render() {
108-
return h('h2', 'Stub Text')
109-
}
110-
})
111-
}
112-
})
113-
114-
// we need to mount everything so that the instance passed to
115-
// transformHArgs isn't null
116-
vm.mount('body')
117-
118-
expect(document.body.outerHTML).toContain('<h1>Root Component</h1>')
119-
})
12060
})

packages/runtime-core/__tests__/vnode.spec.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import {
77
Text,
88
cloneVNode,
99
mergeProps,
10-
normalizeVNode
10+
normalizeVNode,
11+
transformVNodeArgs
1112
} from '../src/vnode'
1213
import { Data } from '../src/component'
1314
import { ShapeFlags, PatchFlags } from '@vue/shared'
15+
import { h } from '../src'
16+
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
1417

1518
describe('vnode', () => {
1619
test('create with just tag', () => {
@@ -335,4 +338,53 @@ describe('vnode', () => {
335338
expect(vnode.dynamicChildren).toStrictEqual([vnode1])
336339
})
337340
})
341+
342+
describe('transformVNodeArgs', () => {
343+
afterEach(() => {
344+
// reset
345+
transformVNodeArgs()
346+
})
347+
348+
test('no-op pass through', () => {
349+
transformVNodeArgs(args => args)
350+
const vnode = createVNode('div', { id: 'foo' }, 'hello')
351+
expect(vnode).toMatchObject({
352+
type: 'div',
353+
props: { id: 'foo' },
354+
children: 'hello',
355+
shapeFlag: ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN
356+
})
357+
})
358+
359+
test('direct override', () => {
360+
transformVNodeArgs(() => ['div', { id: 'foo' }, 'hello'])
361+
const vnode = createVNode('p')
362+
expect(vnode).toMatchObject({
363+
type: 'div',
364+
props: { id: 'foo' },
365+
children: 'hello',
366+
shapeFlag: ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN
367+
})
368+
})
369+
370+
test('receive component instance as 2nd arg', () => {
371+
transformVNodeArgs((args, instance) => {
372+
if (instance) {
373+
return ['h1', null, instance.type.name]
374+
} else {
375+
return args
376+
}
377+
})
378+
const App = {
379+
// this will be the name of the component in the h1
380+
name: 'Root Component',
381+
render() {
382+
return h('p') // this will be overwritten by the transform
383+
}
384+
}
385+
const root = nodeOps.createElement('div')
386+
createApp(App).mount(root)
387+
expect(serializeInner(root)).toBe('<h1>Root Component</h1>')
388+
})
389+
})
338390
})

packages/runtime-core/src/h.ts

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
ComponentOptions
1919
} from './apiOptions'
2020
import { ExtractPropTypes } from './componentProps'
21-
import { currentRenderingInstance } from './componentRenderUtils'
2221

2322
// `h` is a more user-friendly version of `createVNode` that allows omitting the
2423
// props when possible. It is intended for manually written render functions.
@@ -78,52 +77,52 @@ interface Constructor<P = any> {
7877
// manually written render functions.
7978

8079
// element
81-
function _h(type: string, children?: RawChildren): VNode
82-
function _h(
80+
export function h(type: string, children?: RawChildren): VNode
81+
export function h(
8382
type: string,
8483
props?: RawProps | null,
8584
children?: RawChildren
8685
): VNode
8786

8887
// fragment
89-
function _h(type: typeof Fragment, children?: VNodeArrayChildren): VNode
90-
function _h(
88+
export function h(type: typeof Fragment, children?: VNodeArrayChildren): VNode
89+
export function h(
9190
type: typeof Fragment,
9291
props?: RawProps | null,
9392
children?: VNodeArrayChildren
9493
): VNode
9594

9695
// portal (target prop is required)
97-
function _h(
96+
export function h(
9897
type: typeof Portal,
9998
props: RawProps & PortalProps,
10099
children: RawChildren
101100
): VNode
102101

103102
// suspense
104-
function _h(type: typeof Suspense, children?: RawChildren): VNode
105-
function _h(
103+
export function h(type: typeof Suspense, children?: RawChildren): VNode
104+
export function h(
106105
type: typeof Suspense,
107106
props?: (RawProps & SuspenseProps) | null,
108107
children?: RawChildren | RawSlots
109108
): VNode
110109

111110
// functional component
112-
function _h(type: FunctionalComponent, children?: RawChildren): VNode
113-
function _h<P>(
111+
export function h(type: FunctionalComponent, children?: RawChildren): VNode
112+
export function h<P>(
114113
type: FunctionalComponent<P>,
115114
props?: (RawProps & P) | ({} extends P ? null : never),
116115
children?: RawChildren | RawSlots
117116
): VNode
118117

119118
// stateful component
120-
function _h(type: ComponentOptions, children?: RawChildren): VNode
121-
function _h(
119+
export function h(type: ComponentOptions, children?: RawChildren): VNode
120+
export function h(
122121
type: ComponentOptionsWithoutProps | ComponentOptionsWithArrayProps,
123122
props?: RawProps | null,
124123
children?: RawChildren | RawSlots
125124
): VNode
126-
function _h<O>(
125+
export function h<O>(
127126
type: ComponentOptionsWithObjectProps<O>,
128127
props?:
129128
| (RawProps & ExtractPropTypes<O>)
@@ -132,15 +131,15 @@ function _h<O>(
132131
): VNode
133132

134133
// fake constructor type returned by `defineComponent` or class component
135-
function _h(type: Constructor, children?: RawChildren): VNode
136-
function _h<P>(
134+
export function h(type: Constructor, children?: RawChildren): VNode
135+
export function h<P>(
137136
type: Constructor<P>,
138137
props?: (RawProps & P) | ({} extends P ? null : never),
139138
children?: RawChildren | RawSlots
140139
): VNode
141140

142141
// Actual implementation
143-
function _h(type: any, propsOrChildren?: any, children?: any): VNode {
142+
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
144143
if (arguments.length === 2) {
145144
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
146145
// single vnode without props
@@ -160,24 +159,3 @@ function _h(type: any, propsOrChildren?: any, children?: any): VNode {
160159
return createVNode(type, propsOrChildren, children)
161160
}
162161
}
163-
164-
export const h: typeof _h = __DEV__ ? (applyTransformedH as typeof _h) : _h
165-
166-
let argsTransformer: Function | undefined
167-
168-
// This is used to hook into the h function and transform its arguments
169-
// Useful for re-implementing behavior that was previously done with createElement in Vue 2
170-
function applyTransformedH(...args: unknown[]): VNode {
171-
if (argsTransformer) {
172-
args = argsTransformer(args, currentRenderingInstance)
173-
}
174-
return _h(...(args as Parameters<typeof _h>))
175-
}
176-
177-
export function transformHArgs(transformer: Function): void {
178-
argsTransformer = transformer
179-
}
180-
181-
export function resetTransformHArgs(): void {
182-
argsTransformer = undefined
183-
}

packages/runtime-core/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ export { toDisplayString, camelize } from '@vue/shared'
109109
// For integration with runtime compiler
110110
export { registerRuntimeCompiler } from './component'
111111

112+
// For test-utils
113+
export { transformVNodeArgs } from './vnode'
114+
112115
// SSR -------------------------------------------------------------------------
113116

114117
import { createComponentInstance, setupComponent } from './component'

packages/runtime-core/src/vnode.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,32 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
211211
return n1.type === n2.type && n1.key === n2.key
212212
}
213213

214-
export function createVNode(
214+
let vnodeArgsTransformer:
215+
| ((
216+
args: Parameters<typeof _createVNode>,
217+
instance: ComponentInternalInstance | null
218+
) => Parameters<typeof _createVNode>)
219+
| undefined
220+
221+
// Internal API for registering an arguments transform for createVNode
222+
// used for creating stubs in the test-utils
223+
export function transformVNodeArgs(transformer?: typeof vnodeArgsTransformer) {
224+
vnodeArgsTransformer = transformer
225+
}
226+
227+
const createVNodeWithArgsTransform = (
228+
...args: Parameters<typeof _createVNode>
229+
): VNode => {
230+
return _createVNode(
231+
...(vnodeArgsTransformer
232+
? vnodeArgsTransformer(args, currentRenderingInstance)
233+
: args)
234+
)
235+
}
236+
237+
export const createVNode = __DEV__ ? createVNodeWithArgsTransform : _createVNode
238+
239+
function _createVNode(
215240
type: VNodeTypes | ClassComponent,
216241
props: (Data & VNodeProps) | null = null,
217242
children: unknown = null,

0 commit comments

Comments
 (0)