]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-dom): properly stringify v-html/v-text with constant value
authorEvan You <yyx990803@gmail.com>
Fri, 13 May 2022 00:57:43 +0000 (08:57 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 13 May 2022 00:57:43 +0000 (08:57 +0800)
fix #5439
close #5445

packages/compiler-core/src/index.ts
packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap
packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts
packages/compiler-dom/src/transforms/stringifyStatic.ts
packages/compiler-dom/src/transforms/vText.ts

index a68d239581508e821839443fd3289b22a045c9a7..6ed7aa5b8974fbcf824abcd37f5e1d83385077fd 100644 (file)
@@ -59,6 +59,7 @@ export {
   PropsExpression
 } from './transforms/transformElement'
 export { processSlotOutlet } from './transforms/transformSlotOutlet'
+export { getConstantType } from './transforms/hoistStatic'
 export { generateCodeFrame } from '@vue/shared'
 
 // v2 compat only
index efa75df3f47c1d660e866d6658d86cee5923c6b2..8427b38fcecf74aeac7357aab3a2822b5b84b015 100644 (file)
@@ -32,3 +32,23 @@ return function render(_ctx, _cache) {
   return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
 }"
 `;
+
+exports[`stringify static html stringify v-html 1`] = `
+"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
+
+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)
+
+return function render(_ctx, _cache) {
+  return _hoisted_1
+}"
+`;
+
+exports[`stringify static html stringify v-text 1`] = `
+"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
+
+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)
+
+return function render(_ctx, _cache) {
+  return _hoisted_1
+}"
+`;
index 0beb42585b436f089d319f7a81d52e061d6fa647..c737071a827150c5fa23065becf5bd47e31a45c0 100644 (file)
@@ -433,4 +433,25 @@ describe('stringify static html', () => {
       ]
     })
   })
+
+  // #5439
+  test('stringify v-html', () => {
+    const { code } = compileWithStringify(`
+      <pre  data-type="js"><code v-html="'&lt;span&gt;show-it &lt;/span&gt;'"></code></pre>
+      <div class>
+        <span class>1</span><span class>2</span>
+      </div>`)
+    expect(code).toMatch(`<code><span>show-it </span></code>`)
+    expect(code).toMatchSnapshot()
+  })
+
+  test('stringify v-text', () => {
+    const { code } = compileWithStringify(`
+      <pre  data-type="js"><code v-text="'&lt;span&gt;show-it &lt;/span&gt;'"></code></pre>
+      <div class>
+        <span class>1</span><span class>2</span>
+      </div>`)
+    expect(code).toMatch(`<code>&lt;span&gt;show-it &lt;/span&gt;</code>`)
+    expect(code).toMatchSnapshot()
+  })
 })
index d2d0b6fe0783f6f819ab34ddb631691959556150..a268d86cd07a2205d8afcc8ce7c78e4ad41c450f 100644 (file)
@@ -279,6 +279,7 @@ function stringifyElement(
   context: TransformContext
 ): string {
   let res = `<${node.tag}`
+  let innerHTML = ''
   for (let i = 0; i < node.props.length; i++) {
     const p = node.props[i]
     if (p.type === NodeTypes.ATTRIBUTE) {
@@ -286,28 +287,38 @@ function stringifyElement(
       if (p.value) {
         res += `="${escapeHtml(p.value.content)}"`
       }
-    } else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
-      const exp = p.exp as SimpleExpressionNode
-      if (exp.content[0] === '_') {
-        // internally generated string constant references
-        // e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
-        res += ` ${(p.arg as SimpleExpressionNode).content}="__VUE_EXP_START__${
-          exp.content
-        }__VUE_EXP_END__"`
-        continue
-      }
-      // constant v-bind, e.g. :foo="1"
-      let evaluated = evaluateConstant(exp)
-      if (evaluated != null) {
-        const arg = p.arg && (p.arg as SimpleExpressionNode).content
-        if (arg === 'class') {
-          evaluated = normalizeClass(evaluated)
-        } else if (arg === 'style') {
-          evaluated = stringifyStyle(normalizeStyle(evaluated))
+    } else if (p.type === NodeTypes.DIRECTIVE) {
+      if (p.name === 'bind') {
+        const exp = p.exp as SimpleExpressionNode
+        if (exp.content[0] === '_') {
+          // internally generated string constant references
+          // e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
+          res += ` ${
+            (p.arg as SimpleExpressionNode).content
+          }="__VUE_EXP_START__${exp.content}__VUE_EXP_END__"`
+          continue
+        }
+        // constant v-bind, e.g. :foo="1"
+        let evaluated = evaluateConstant(exp)
+        if (evaluated != null) {
+          const arg = p.arg && (p.arg as SimpleExpressionNode).content
+          if (arg === 'class') {
+            evaluated = normalizeClass(evaluated)
+          } else if (arg === 'style') {
+            evaluated = stringifyStyle(normalizeStyle(evaluated))
+          }
+          res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
+            evaluated
+          )}"`
         }
-        res += ` ${(p.arg as SimpleExpressionNode).content}="${escapeHtml(
-          evaluated
-        )}"`
+      } else if (p.name === 'html') {
+        // #5439 v-html with constant value
+        // not sure why would anyone do this but it can happen
+        innerHTML = evaluateConstant(p.exp as SimpleExpressionNode)
+      } else if (p.name === 'text') {
+        innerHTML = escapeHtml(
+          toDisplayString(evaluateConstant(p.exp as SimpleExpressionNode))
+        )
       }
     }
   }
@@ -315,8 +326,12 @@ function stringifyElement(
     res += ` ${context.scopeId}`
   }
   res += `>`
-  for (let i = 0; i < node.children.length; i++) {
-    res += stringifyNode(node.children[i], context)
+  if (innerHTML) {
+    res += innerHTML
+  } else {
+    for (let i = 0; i < node.children.length; i++) {
+      res += stringifyNode(node.children[i], context)
+    }
   }
   if (!isVoidTag(node.tag)) {
     res += `</${node.tag}>`
index 862d2c2040cc54c6006d1e86ad0e1a9d7cf1e235..77bf0032b8e4a17142ba88537ae375f4808ed86e 100644 (file)
@@ -3,7 +3,8 @@ import {
   createObjectProperty,
   createSimpleExpression,
   TO_DISPLAY_STRING,
-  createCallExpression
+  createCallExpression,
+  getConstantType
 } from '@vue/compiler-core'
 import { createDOMCompilerError, DOMErrorCodes } from '../errors'
 
@@ -25,11 +26,13 @@ export const transformVText: DirectiveTransform = (dir, node, context) => {
       createObjectProperty(
         createSimpleExpression(`textContent`, true),
         exp
-          ? createCallExpression(
-              context.helperString(TO_DISPLAY_STRING),
-              [exp],
-              loc
-            )
+          ? getConstantType(exp, context) > 0
+            ? exp
+            : createCallExpression(
+                context.helperString(TO_DISPLAY_STRING),
+                [exp],
+                loc
+              )
           : createSimpleExpression('', true)
       )
     ]