Skip to content

Commit 066ba82

Browse files
committed
wip(ssr): escape helpers
1 parent 4e40d0d commit 066ba82

File tree

10 files changed

+81
-23
lines changed

10 files changed

+81
-23
lines changed

packages/compiler-core/__tests__/codegen.spec.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '../src'
1919
import {
2020
CREATE_VNODE,
21-
TO_STRING,
21+
TO_DISPLAY_STRING,
2222
RESOLVE_DIRECTIVE,
2323
helperNameMap,
2424
RESOLVE_COMPONENT,
@@ -164,7 +164,7 @@ describe('compiler: codegen', () => {
164164
codegenNode: createInterpolation(`hello`, locStub)
165165
})
166166
)
167-
expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
167+
expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
168168
expect(code).toMatchSnapshot()
169169
})
170170

@@ -197,7 +197,9 @@ describe('compiler: codegen', () => {
197197
])
198198
})
199199
)
200-
expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
200+
expect(code).toMatch(
201+
`return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar)`
202+
)
201203
expect(code).toMatchSnapshot()
202204
})
203205

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '../src/ast'
99
import { ErrorCodes, createCompilerError } from '../src/errors'
1010
import {
11-
TO_STRING,
11+
TO_DISPLAY_STRING,
1212
OPEN_BLOCK,
1313
CREATE_BLOCK,
1414
FRAGMENT,
@@ -227,7 +227,7 @@ describe('compiler: transform', () => {
227227
test('should inject toString helper for interpolations', () => {
228228
const ast = baseParse(`{{ foo }}`)
229229
transform(ast, {})
230-
expect(ast.helpers).toContain(TO_STRING)
230+
expect(ast.helpers).toContain(TO_DISPLAY_STRING)
231231
})
232232

233233
test('should inject createVNode and Comment for comments', () => {

packages/compiler-core/src/codegen.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
import { isString, isArray, isSymbol } from '@vue/shared'
3232
import {
3333
helperNameMap,
34-
TO_STRING,
34+
TO_DISPLAY_STRING,
3535
CREATE_VNODE,
3636
RESOLVE_COMPONENT,
3737
RESOLVE_DIRECTIVE,
@@ -491,7 +491,7 @@ function genExpression(node: SimpleExpressionNode, context: CodegenContext) {
491491

492492
function genInterpolation(node: InterpolationNode, context: CodegenContext) {
493493
const { push, helper } = context
494-
push(`${helper(TO_STRING)}(`)
494+
push(`${helper(TO_DISPLAY_STRING)}(`)
495495
genNode(node.content, context)
496496
push(`)`)
497497
}

packages/compiler-core/src/runtimeHelpers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const WITH_DIRECTIVES = Symbol(__DEV__ ? `withDirectives` : ``)
1717
export const RENDER_LIST = Symbol(__DEV__ ? `renderList` : ``)
1818
export const RENDER_SLOT = Symbol(__DEV__ ? `renderSlot` : ``)
1919
export const CREATE_SLOTS = Symbol(__DEV__ ? `createSlots` : ``)
20-
export const TO_STRING = Symbol(__DEV__ ? `toString` : ``)
20+
export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ``)
2121
export const MERGE_PROPS = Symbol(__DEV__ ? `mergeProps` : ``)
2222
export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``)
2323
export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
@@ -47,7 +47,7 @@ export const helperNameMap: any = {
4747
[RENDER_LIST]: `renderList`,
4848
[RENDER_SLOT]: `renderSlot`,
4949
[CREATE_SLOTS]: `createSlots`,
50-
[TO_STRING]: `toString`,
50+
[TO_DISPLAY_STRING]: `toDisplayString`,
5151
[MERGE_PROPS]: `mergeProps`,
5252
[TO_HANDLERS]: `toHandlers`,
5353
[CAMELIZE]: `camelize`,

packages/compiler-core/src/transform.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '@vue/shared'
2828
import { defaultOnError } from './errors'
2929
import {
30-
TO_STRING,
30+
TO_DISPLAY_STRING,
3131
FRAGMENT,
3232
helperNameMap,
3333
WITH_DIRECTIVES,
@@ -365,7 +365,7 @@ export function traverseNode(
365365
break
366366
case NodeTypes.INTERPOLATION:
367367
// no need to traverse, but we need to inject toString helper
368-
context.helper(TO_STRING)
368+
context.helper(TO_DISPLAY_STRING)
369369
break
370370

371371
// for container types, further traverse downwards

packages/runtime-core/src/helpers/toString.ts

-10
This file was deleted.

packages/runtime-core/src/index.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ export {
8181
resolveDynamicComponent
8282
} from './helpers/resolveAssets'
8383
export { renderList } from './helpers/renderList'
84-
export { toString } from './helpers/toString'
8584
export { toHandlers } from './helpers/toHandlers'
8685
export { renderSlot } from './helpers/renderSlot'
8786
export { createSlots } from './helpers/createSlots'
@@ -90,7 +89,12 @@ export { setBlockTracking, createTextVNode, createCommentVNode } from './vnode'
9089
// Since @vue/shared is inlined into final builds,
9190
// when re-exporting from @vue/shared we need to avoid relying on their original
9291
// types so that the bundled d.ts does not attempt to import from it.
93-
import { capitalize as _capitalize, camelize as _camelize } from '@vue/shared'
92+
import {
93+
toDisplayString as _toDisplayString,
94+
capitalize as _capitalize,
95+
camelize as _camelize
96+
} from '@vue/shared'
97+
export const toDisplayString = _toDisplayString as (s: unknown) => string
9498
export const capitalize = _capitalize as (s: string) => string
9599
export const camelize = _camelize as (s: string) => string
96100

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { toDisplayString } from '@vue/shared'
2+
3+
const escapeRE = /["'&<>]/
4+
5+
export function escape(string: unknown) {
6+
const str = '' + string
7+
const match = escapeRE.exec(str)
8+
9+
if (!match) {
10+
return str
11+
}
12+
13+
let html = ''
14+
let escaped: string
15+
let index: number
16+
let lastIndex = 0
17+
for (index = match.index; index < str.length; index++) {
18+
switch (str.charCodeAt(index)) {
19+
case 34: // "
20+
escaped = '&quot;'
21+
break
22+
case 38: // &
23+
escaped = '&amp;'
24+
break
25+
case 39: // '
26+
escaped = '&#39;'
27+
break
28+
case 60: // <
29+
escaped = '&lt;'
30+
break
31+
case 62: // >
32+
escaped = '&gt;'
33+
break
34+
default:
35+
continue
36+
}
37+
38+
if (lastIndex !== index) {
39+
html += str.substring(lastIndex, index)
40+
}
41+
42+
lastIndex = index + 1
43+
html += escaped
44+
}
45+
46+
return lastIndex !== index ? html + str.substring(lastIndex, index) : html
47+
}
48+
49+
export function interpolate(value: unknown) {
50+
return escape(toDisplayString(value))
51+
}

packages/server-renderer/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from 'vue'
1010
import { isString, isPromise, isArray } from '@vue/shared'
1111

12+
export * from './helpers'
13+
1214
type SSRBuffer = SSRBufferItem[]
1315
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
1416
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]

packages/shared/src/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,12 @@ export const capitalize = cacheStringFunction(
9898
// compare whether a value has changed, accounting for NaN.
9999
export const hasChanged = (value: any, oldValue: any): boolean =>
100100
value !== oldValue && (value === value || oldValue === oldValue)
101+
102+
// for converting {{ interpolation }} values to displayed strings.
103+
export function toDisplayString(val: unknown): string {
104+
return val == null
105+
? ''
106+
: isArray(val) || (isPlainObject(val) && val.toString === objectToString)
107+
? JSON.stringify(val, null, 2)
108+
: String(val)
109+
}

0 commit comments

Comments
 (0)