} from '../src'
import {
CREATE_VNODE,
- TO_STRING,
+ TO_DISPLAY_STRING,
RESOLVE_DIRECTIVE,
helperNameMap,
RESOLVE_COMPONENT,
codegenNode: createInterpolation(`hello`, locStub)
})
)
- expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
+ expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
expect(code).toMatchSnapshot()
})
])
})
)
- expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
+ expect(code).toMatch(
+ `return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar)`
+ )
expect(code).toMatchSnapshot()
})
} from '../src/ast'
import { ErrorCodes, createCompilerError } from '../src/errors'
import {
- TO_STRING,
+ TO_DISPLAY_STRING,
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
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', () => {
import { isString, isArray, isSymbol } from '@vue/shared'
import {
helperNameMap,
- TO_STRING,
+ TO_DISPLAY_STRING,
CREATE_VNODE,
RESOLVE_COMPONENT,
RESOLVE_DIRECTIVE,
function genInterpolation(node: InterpolationNode, context: CodegenContext) {
const { push, helper } = context
- push(`${helper(TO_STRING)}(`)
+ push(`${helper(TO_DISPLAY_STRING)}(`)
genNode(node.content, context)
push(`)`)
}
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` : ``)
[RENDER_LIST]: `renderList`,
[RENDER_SLOT]: `renderSlot`,
[CREATE_SLOTS]: `createSlots`,
- [TO_STRING]: `toString`,
+ [TO_DISPLAY_STRING]: `toDisplayString`,
[MERGE_PROPS]: `mergeProps`,
[TO_HANDLERS]: `toHandlers`,
[CAMELIZE]: `camelize`,
} from '@vue/shared'
import { defaultOnError } from './errors'
import {
- TO_STRING,
+ TO_DISPLAY_STRING,
FRAGMENT,
helperNameMap,
WITH_DIRECTIVES,
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
+++ /dev/null
-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)
-}
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'
// 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
--- /dev/null
+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 = '"'
+ break
+ case 38: // &
+ escaped = '&'
+ break
+ case 39: // '
+ escaped = '''
+ break
+ case 60: // <
+ escaped = '<'
+ break
+ case 62: // >
+ escaped = '>'
+ 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))
+}
} 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)[]
// 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)
+}