]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(ssr): escape helpers
authorEvan You <yyx990803@gmail.com>
Sun, 26 Jan 2020 22:35:21 +0000 (17:35 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 27 Jan 2020 21:00:18 +0000 (16:00 -0500)
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/transform.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/runtimeHelpers.ts
packages/compiler-core/src/transform.ts
packages/runtime-core/src/helpers/toString.ts [deleted file]
packages/runtime-core/src/index.ts
packages/server-renderer/src/helpers.ts [new file with mode: 0644]
packages/server-renderer/src/index.ts
packages/shared/src/index.ts

index 2a116bb771fa210e1a29bdff6636e15099bb5c4c..ef202ef2997201d23541b97052e40b96b8ff116a 100644 (file)
@@ -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()
   })
 
index 2e1c1e803c44e32567a5e475b9da038435eb4217..d1b2bbd03823dde0e57c8ec83be2883ecc41920f 100644 (file)
@@ -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', () => {
index d235263640fef51fb90719df6d567bcbf695ef73..a8dcb558bf9cc25d2411379264efafb7169d1348 100644 (file)
@@ -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(`)`)
 }
index 00b84f2f0ab5e081a34862f6d616d7da92da0f26..b684780214c4f7d111b47165b72bec5de9aac9c4 100644 (file)
@@ -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`,
index 4bfd749fc678766e3bfebec56699a57164216020..7029c0851a133a2e2b68d59ff15e53a8d9884b4b 100644 (file)
@@ -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
diff --git a/packages/runtime-core/src/helpers/toString.ts b/packages/runtime-core/src/helpers/toString.ts
deleted file mode 100644 (file)
index 968f51d..0000000
+++ /dev/null
@@ -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)
-}
index 2976c53a1ebc478ae0f5e4b55d4033da693fbc26..d3b24f2bdde422cb9f03ac172f5b246a3efcd1c5 100644 (file)
@@ -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
 
diff --git a/packages/server-renderer/src/helpers.ts b/packages/server-renderer/src/helpers.ts
new file mode 100644 (file)
index 0000000..761dc10
--- /dev/null
@@ -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))
+}
index 19034429f6f732fe5e7fa41ed84910b3fd4a8fd3..318a0fa3495d7136c4e23f576be8450297242718 100644 (file)
@@ -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)[]
index 4d6f700166f0727e4bf375d7fee26973c2b2a65f..89f21923e8601f8ca1373a1202861c2ea6ebb5ac 100644 (file)
@@ -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)
+}