Skip to content

Commit a64867a

Browse files
committed
feat(runtime-vapor): dynamic components work with v-html and v-text
1 parent 88ef97f commit a64867a

File tree

11 files changed

+96
-10
lines changed

11 files changed

+96
-10
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/vHtml.spec.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,13 @@ export function render(_ctx) {
3232
return n0
3333
}"
3434
`;
35+
36+
exports[`v-html > work with dynamic component 1`] = `
37+
"import { createDynamicComponent as _createDynamicComponent, setHtml as _setHtml, renderEffect as _renderEffect } from 'vue';
38+
39+
export function render(_ctx) {
40+
const n0 = _createDynamicComponent(() => ('button'), null, null, true)
41+
_renderEffect(() => _setHtml(n0.nodes, _ctx.foo))
42+
return n0
43+
}"
44+
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vText.spec.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,13 @@ export function render(_ctx) {
3333
return n0
3434
}"
3535
`;
36+
37+
exports[`v-text > work with dynamic component 1`] = `
38+
"import { createDynamicComponent as _createDynamicComponent, toDisplayString as _toDisplayString, setElementText as _setElementText, renderEffect as _renderEffect } from 'vue';
39+
40+
export function render(_ctx) {
41+
const n0 = _createDynamicComponent(() => ('button'), null, null, true)
42+
_renderEffect(() => _setElementText(n0.nodes, _toDisplayString(_ctx.foo), true))
43+
return n0
44+
}"
45+
`;

packages/compiler-vapor/__tests__/transforms/vHtml.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ describe('v-html', () => {
5454
expect(code).matchSnapshot()
5555
})
5656

57+
test('work with dynamic component', () => {
58+
const { code } = compileWithVHtml(
59+
`<component :is="'button'" v-html="foo"/>`,
60+
)
61+
expect(code).matchSnapshot()
62+
expect(code).contains('setHtml(n0.nodes, _ctx.foo))')
63+
})
64+
5765
test('should raise error and ignore children when v-html is present', () => {
5866
const onError = vi.fn()
5967
const { code, ir, helpers } = compileWithVHtml(

packages/compiler-vapor/__tests__/transforms/vText.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ describe('v-text', () => {
5858
expect(code).matchSnapshot()
5959
})
6060

61+
test('work with dynamic component', () => {
62+
const { code } = compileWithVText(
63+
`<component :is="'button'" v-text="foo"/>`,
64+
)
65+
expect(code).matchSnapshot()
66+
expect(code).contains(
67+
'setElementText(n0.nodes, _toDisplayString(_ctx.foo), true)',
68+
)
69+
})
70+
6171
test('should raise error and ignore children when v-text is present', () => {
6272
const onError = vi.fn()
6373
const { code, ir } = compileWithVText(`<div v-text="test">hello</div>`, {

packages/compiler-vapor/src/generators/component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,15 @@ export function genCreateComponent(
6060
[],
6161
)
6262

63+
const isDynamicComponent = operation.dynamic && !operation.dynamic.isStatic
64+
if (isDynamicComponent) context.block.dynamicComponents.push(operation.id)
65+
6366
return [
6467
NEWLINE,
6568
...inlineHandlers,
6669
`const n${operation.id} = `,
6770
...genCall(
68-
operation.dynamic && !operation.dynamic.isStatic
71+
isDynamicComponent
6972
? helper('createDynamicComponent')
7073
: operation.asset
7174
? helper('createComponentWithFallback')

packages/compiler-vapor/src/generators/html.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,21 @@ export function genSetHtml(
77
oper: SetHtmlIRNode,
88
context: CodegenContext,
99
): CodeFragment[] {
10-
const { helper } = context
10+
const {
11+
helper,
12+
block: { dynamicComponents },
13+
} = context
14+
15+
const isDynamicComponent = dynamicComponents.includes(oper.element)
1116
const { value, element } = oper
1217
return [
1318
NEWLINE,
14-
...genCall(helper('setHtml'), `n${element}`, genExpression(value, context)),
19+
...genCall(
20+
helper('setHtml'),
21+
// if the element is a dynamic component (VaporFragment)
22+
// it should set html to the VaporFragment's nodes
23+
`n${element}${isDynamicComponent ? '.nodes' : ''}`,
24+
genExpression(value, context),
25+
),
1526
]
1627
}

packages/compiler-vapor/src/generators/text.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,33 @@ export function genSetText(
99
oper: SetTextIRNode,
1010
context: CodegenContext,
1111
): CodeFragment[] {
12-
const { helper } = context
12+
const {
13+
helper,
14+
block: { dynamicComponents },
15+
} = context
1316
const { element, values, generated, jsx } = oper
1417
const texts = combineValues(values, context, jsx)
15-
return [
16-
NEWLINE,
17-
...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts),
18-
]
18+
19+
// if the element is a dynamic component, we need to use `setElementText`
20+
// to set the textContent of the VaporFragment's nodes.
21+
return dynamicComponents.includes(oper.element)
22+
? [
23+
NEWLINE,
24+
...genCall(
25+
helper('setElementText'),
26+
`n${element}.nodes`,
27+
texts,
28+
'true', // isConverted
29+
),
30+
]
31+
: [
32+
NEWLINE,
33+
...genCall(
34+
helper('setText'),
35+
`${generated ? 'x' : 'n'}${element}`,
36+
texts,
37+
),
38+
]
1939
}
2040

2141
function combineValues(
@@ -40,6 +60,14 @@ export function genGetTextChild(
4060
oper: GetTextChildIRNode,
4161
context: CodegenContext,
4262
): CodeFragment[] {
63+
const {
64+
block: { dynamicComponents },
65+
} = context
66+
67+
// if the parent is a dynamic component, don't need to generate a child
68+
// because it will use the `setElementText` helper directly.
69+
if (dynamicComponents.includes(oper.parent)) return []
70+
4371
return [
4472
NEWLINE,
4573
`const x${oper.parent} = ${context.helper('child')}(n${oper.parent})`,

packages/compiler-vapor/src/ir/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export interface BlockIRNode extends BaseIRNode {
4949
type: IRNodeTypes.BLOCK
5050
node: RootNode | TemplateChildNode
5151
dynamic: IRDynamicInfo
52+
dynamicComponents: number[]
5253
tempId: number
5354
effect: IREffect[]
5455
operation: OperationNode[]

packages/compiler-vapor/src/transforms/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
2626
type: IRNodeTypes.BLOCK,
2727
node,
2828
dynamic: newDynamic(),
29+
dynamicComponents: [],
2930
effect: [],
3031
operation: [],
3132
returns: [],

packages/runtime-vapor/src/dom/prop.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,16 @@ export function setText(el: Text & { $txt?: string }, value: string): void {
185185
}
186186

187187
/**
188-
* Used by setDynamicProps only, so need to guard with `toDisplayString`
188+
* Used by
189+
* - setDynamicProps, need to guard with `toDisplayString`
190+
* - v-text on dynamic component, value passed here is already converted
189191
*/
190192
export function setElementText(
191193
el: Node & { $txt?: string },
192194
value: unknown,
195+
isConverted: boolean = false,
193196
): void {
194-
if (el.$txt !== (value = toDisplayString(value))) {
197+
if (el.$txt !== (value = isConverted ? value : toDisplayString(value))) {
195198
el.textContent = el.$txt = value as string
196199
}
197200
}

0 commit comments

Comments
 (0)