Evan You 6 лет назад
Родитель
Сommit
066ba82c7f

+ 5 - 3
packages/compiler-core/__tests__/codegen.spec.ts

@@ -18,7 +18,7 @@ import {
 } from '../src'
 import {
   CREATE_VNODE,
-  TO_STRING,
+  TO_DISPLAY_STRING,
   RESOLVE_DIRECTIVE,
   helperNameMap,
   RESOLVE_COMPONENT,
@@ -164,7 +164,7 @@ describe('compiler: codegen', () => {
         codegenNode: createInterpolation(`hello`, locStub)
       })
     )
-    expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
+    expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
     expect(code).toMatchSnapshot()
   })
 
@@ -197,7 +197,9 @@ describe('compiler: codegen', () => {
         ])
       })
     )
-    expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
+    expect(code).toMatch(
+      `return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar)`
+    )
     expect(code).toMatchSnapshot()
   })
 

+ 2 - 2
packages/compiler-core/__tests__/transform.spec.ts

@@ -8,7 +8,7 @@ import {
 } from '../src/ast'
 import { ErrorCodes, createCompilerError } from '../src/errors'
 import {
-  TO_STRING,
+  TO_DISPLAY_STRING,
   OPEN_BLOCK,
   CREATE_BLOCK,
   FRAGMENT,
@@ -227,7 +227,7 @@ describe('compiler: transform', () => {
   test('should inject toString helper for interpolations', () => {
     const ast = baseParse(`{{ foo }}`)
     transform(ast, {})
-    expect(ast.helpers).toContain(TO_STRING)
+    expect(ast.helpers).toContain(TO_DISPLAY_STRING)
   })
 
   test('should inject createVNode and Comment for comments', () => {

+ 2 - 2
packages/compiler-core/src/codegen.ts

@@ -31,7 +31,7 @@ import {
 import { isString, isArray, isSymbol } from '@vue/shared'
 import {
   helperNameMap,
-  TO_STRING,
+  TO_DISPLAY_STRING,
   CREATE_VNODE,
   RESOLVE_COMPONENT,
   RESOLVE_DIRECTIVE,
@@ -491,7 +491,7 @@ function genExpression(node: SimpleExpressionNode, context: CodegenContext) {
 
 function genInterpolation(node: InterpolationNode, context: CodegenContext) {
   const { push, helper } = context
-  push(`${helper(TO_STRING)}(`)
+  push(`${helper(TO_DISPLAY_STRING)}(`)
   genNode(node.content, context)
   push(`)`)
 }

+ 2 - 2
packages/compiler-core/src/runtimeHelpers.ts

@@ -17,7 +17,7 @@ export const WITH_DIRECTIVES = Symbol(__DEV__ ? `withDirectives` : ``)
 export const RENDER_LIST = Symbol(__DEV__ ? `renderList` : ``)
 export const RENDER_SLOT = Symbol(__DEV__ ? `renderSlot` : ``)
 export const CREATE_SLOTS = Symbol(__DEV__ ? `createSlots` : ``)
-export const TO_STRING = Symbol(__DEV__ ? `toString` : ``)
+export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ``)
 export const MERGE_PROPS = Symbol(__DEV__ ? `mergeProps` : ``)
 export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``)
 export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
@@ -47,7 +47,7 @@ export const helperNameMap: any = {
   [RENDER_LIST]: `renderList`,
   [RENDER_SLOT]: `renderSlot`,
   [CREATE_SLOTS]: `createSlots`,
-  [TO_STRING]: `toString`,
+  [TO_DISPLAY_STRING]: `toDisplayString`,
   [MERGE_PROPS]: `mergeProps`,
   [TO_HANDLERS]: `toHandlers`,
   [CAMELIZE]: `camelize`,

+ 2 - 2
packages/compiler-core/src/transform.ts

@@ -27,7 +27,7 @@ import {
 } from '@vue/shared'
 import { defaultOnError } from './errors'
 import {
-  TO_STRING,
+  TO_DISPLAY_STRING,
   FRAGMENT,
   helperNameMap,
   WITH_DIRECTIVES,
@@ -365,7 +365,7 @@ export function traverseNode(
       break
     case NodeTypes.INTERPOLATION:
       // no need to traverse, but we need to inject toString helper
-      context.helper(TO_STRING)
+      context.helper(TO_DISPLAY_STRING)
       break
 
     // for container types, further traverse downwards

+ 0 - 10
packages/runtime-core/src/helpers/toString.ts

@@ -1,10 +0,0 @@
-import { isArray, isPlainObject, objectToString } from '@vue/shared'
-
-// for converting {{ interpolation }} values to displayed strings.
-export function toString(val: unknown): string {
-  return val == null
-    ? ''
-    : isArray(val) || (isPlainObject(val) && val.toString === objectToString)
-      ? JSON.stringify(val, null, 2)
-      : String(val)
-}

+ 6 - 2
packages/runtime-core/src/index.ts

@@ -81,7 +81,6 @@ export {
   resolveDynamicComponent
 } from './helpers/resolveAssets'
 export { renderList } from './helpers/renderList'
-export { toString } from './helpers/toString'
 export { toHandlers } from './helpers/toHandlers'
 export { renderSlot } from './helpers/renderSlot'
 export { createSlots } from './helpers/createSlots'
@@ -90,7 +89,12 @@ export { setBlockTracking, createTextVNode, createCommentVNode } from './vnode'
 // Since @vue/shared is inlined into final builds,
 // when re-exporting from @vue/shared we need to avoid relying on their original
 // types so that the bundled d.ts does not attempt to import from it.
-import { capitalize as _capitalize, camelize as _camelize } from '@vue/shared'
+import {
+  toDisplayString as _toDisplayString,
+  capitalize as _capitalize,
+  camelize as _camelize
+} from '@vue/shared'
+export const toDisplayString = _toDisplayString as (s: unknown) => string
 export const capitalize = _capitalize as (s: string) => string
 export const camelize = _camelize as (s: string) => string
 

+ 51 - 0
packages/server-renderer/src/helpers.ts

@@ -0,0 +1,51 @@
+import { toDisplayString } from '@vue/shared'
+
+const escapeRE = /["'&<>]/
+
+export function escape(string: unknown) {
+  const str = '' + string
+  const match = escapeRE.exec(str)
+
+  if (!match) {
+    return str
+  }
+
+  let html = ''
+  let escaped: string
+  let index: number
+  let lastIndex = 0
+  for (index = match.index; index < str.length; index++) {
+    switch (str.charCodeAt(index)) {
+      case 34: // "
+        escaped = '&quot;'
+        break
+      case 38: // &
+        escaped = '&amp;'
+        break
+      case 39: // '
+        escaped = '&#39;'
+        break
+      case 60: // <
+        escaped = '&lt;'
+        break
+      case 62: // >
+        escaped = '&gt;'
+        break
+      default:
+        continue
+    }
+
+    if (lastIndex !== index) {
+      html += str.substring(lastIndex, index)
+    }
+
+    lastIndex = index + 1
+    html += escaped
+  }
+
+  return lastIndex !== index ? html + str.substring(lastIndex, index) : html
+}
+
+export function interpolate(value: unknown) {
+  return escape(toDisplayString(value))
+}

+ 2 - 0
packages/server-renderer/src/index.ts

@@ -9,6 +9,8 @@ import {
 } from 'vue'
 import { isString, isPromise, isArray } from '@vue/shared'
 
+export * from './helpers'
+
 type SSRBuffer = SSRBufferItem[]
 type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
 type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]

+ 9 - 0
packages/shared/src/index.ts

@@ -98,3 +98,12 @@ export const capitalize = cacheStringFunction(
 // compare whether a value has changed, accounting for NaN.
 export const hasChanged = (value: any, oldValue: any): boolean =>
   value !== oldValue && (value === value || oldValue === oldValue)
+
+// for converting {{ interpolation }} values to displayed strings.
+export function toDisplayString(val: unknown): string {
+  return val == null
+    ? ''
+    : isArray(val) || (isPlainObject(val) && val.toString === objectToString)
+      ? JSON.stringify(val, null, 2)
+      : String(val)
+}