Skip to content

Commit 6283b2e

Browse files
committed
fix(compiler-dom): properly stringify v-html/v-text with constant value
fix #5439 close #5445
1 parent cd92654 commit 6283b2e

File tree

5 files changed

+89
-29
lines changed

5 files changed

+89
-29
lines changed

packages/compiler-core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export {
5959
PropsExpression
6060
} from './transforms/transformElement'
6161
export { processSlotOutlet } from './transforms/transformSlotOutlet'
62+
export { getConstantType } from './transforms/hoistStatic'
6263
export { generateCodeFrame } from '@vue/shared'
6364

6465
// v2 compat only

packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap

+20
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,23 @@ return function render(_ctx, _cache) {
3232
return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
3333
}"
3434
`;
35+
36+
exports[`stringify static html stringify v-html 1`] = `
37+
"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
38+
39+
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<pre data-type=\\\\\\"js\\\\\\"><code><span>show-it </span></code></pre><div class><span class>1</span><span class>2</span></div>\\", 2)
40+
41+
return function render(_ctx, _cache) {
42+
return _hoisted_1
43+
}"
44+
`;
45+
46+
exports[`stringify static html stringify v-text 1`] = `
47+
"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
48+
49+
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<pre data-type=\\\\\\"js\\\\\\"><code>&lt;span&gt;show-it &lt;/span&gt;</code></pre><div class><span class>1</span><span class>2</span></div>\\", 2)
50+
51+
return function render(_ctx, _cache) {
52+
return _hoisted_1
53+
}"
54+
`;

packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,25 @@ describe('stringify static html', () => {
433433
]
434434
})
435435
})
436+
437+
// #5439
438+
test('stringify v-html', () => {
439+
const { code } = compileWithStringify(`
440+
<pre data-type="js"><code v-html="'&lt;span&gt;show-it &lt;/span&gt;'"></code></pre>
441+
<div class>
442+
<span class>1</span><span class>2</span>
443+
</div>`)
444+
expect(code).toMatch(`<code><span>show-it </span></code>`)
445+
expect(code).toMatchSnapshot()
446+
})
447+
448+
test('stringify v-text', () => {
449+
const { code } = compileWithStringify(`
450+
<pre data-type="js"><code v-text="'&lt;span&gt;show-it &lt;/span&gt;'"></code></pre>
451+
<div class>
452+
<span class>1</span><span class>2</span>
453+
</div>`)
454+
expect(code).toMatch(`<code>&lt;span&gt;show-it &lt;/span&gt;</code>`)
455+
expect(code).toMatchSnapshot()
456+
})
436457
})

packages/compiler-dom/src/transforms/stringifyStatic.ts

+38-23
Original file line numberDiff line numberDiff line change
@@ -279,44 +279,59 @@ function stringifyElement(
279279
context: TransformContext
280280
): string {
281281
let res = `<${node.tag}`
282+
let innerHTML = ''
282283
for (let i = 0; i < node.props.length; i++) {
283284
const p = node.props[i]
284285
if (p.type === NodeTypes.ATTRIBUTE) {
285286
res += ` ${p.name}`
286287
if (p.value) {
287288
res += `="${escapeHtml(p.value.content)}"`
288289
}
289-
} else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
290-
const exp = p.exp as SimpleExpressionNode
291-
if (exp.content[0] === '_') {
292-
// internally generated string constant references
293-
// e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
294-
res += ` ${(p.arg as SimpleExpressionNode).content}="__VUE_EXP_START__${
295-
exp.content
296-
}__VUE_EXP_END__"`
297-
continue
298-
}
299-
// constant v-bind, e.g. :foo="1"
300-
let evaluated = evaluateConstant(exp)
301-
if (evaluated != null) {
302-
const arg = p.arg && (p.arg as SimpleExpressionNode).content
303-
if (arg === 'class') {
304-
evaluated = normalizeClass(evaluated)
305-
} else if (arg === 'style') {
306-
evaluated = stringifyStyle(normalizeStyle(evaluated))
290+
} else if (p.type === NodeTypes.DIRECTIVE) {
291+
if (p.name === 'bind') {
292+
const exp = p.exp as SimpleExpressionNode
293+
if (exp.content[0] === '_') {
294+
// internally generated string constant references
295+
// e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
296+
res += ` ${
297+
(p.arg as SimpleExpressionNode).content
298+
}="__VUE_EXP_START__${exp.content}__VUE_EXP_END__"`
299+
continue
300+
}
301+
// constant v-bind, e.g. :foo="1"
302+
let evaluated = evaluateConstant(exp)
303+
if (evaluated != null) {
304+
const arg = p.arg && (p.arg as SimpleExpressionNode).content
305+
if (arg === 'class') {
306+
evaluated = normalizeClass(evaluated)
307+
} else if (arg === 'style') {
308+
evaluated = stringifyStyle(normalizeStyle(evaluated))
309+
}
310+
res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
311+
evaluated
312+
)}"`
307313
}
308-
res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
309-
evaluated
310-
)}"`
314+
} else if (p.name === 'html') {
315+
// #5439 v-html with constant value
316+
// not sure why would anyone do this but it can happen
317+
innerHTML = evaluateConstant(p.exp as SimpleExpressionNode)
318+
} else if (p.name === 'text') {
319+
innerHTML = escapeHtml(
320+
toDisplayString(evaluateConstant(p.exp as SimpleExpressionNode))
321+
)
311322
}
312323
}
313324
}
314325
if (context.scopeId) {
315326
res += ` ${context.scopeId}`
316327
}
317328
res += `>`
318-
for (let i = 0; i < node.children.length; i++) {
319-
res += stringifyNode(node.children[i], context)
329+
if (innerHTML) {
330+
res += innerHTML
331+
} else {
332+
for (let i = 0; i < node.children.length; i++) {
333+
res += stringifyNode(node.children[i], context)
334+
}
320335
}
321336
if (!isVoidTag(node.tag)) {
322337
res += `</${node.tag}>`

packages/compiler-dom/src/transforms/vText.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
createObjectProperty,
44
createSimpleExpression,
55
TO_DISPLAY_STRING,
6-
createCallExpression
6+
createCallExpression,
7+
getConstantType
78
} from '@vue/compiler-core'
89
import { createDOMCompilerError, DOMErrorCodes } from '../errors'
910

@@ -25,11 +26,13 @@ export const transformVText: DirectiveTransform = (dir, node, context) => {
2526
createObjectProperty(
2627
createSimpleExpression(`textContent`, true),
2728
exp
28-
? createCallExpression(
29-
context.helperString(TO_DISPLAY_STRING),
30-
[exp],
31-
loc
32-
)
29+
? getConstantType(exp, context) > 0
30+
? exp
31+
: createCallExpression(
32+
context.helperString(TO_DISPLAY_STRING),
33+
[exp],
34+
loc
35+
)
3336
: createSimpleExpression('', true)
3437
)
3538
]

0 commit comments

Comments
 (0)