From 3c60d40827f65cbff024cfda4bb981a742bb83a7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 4 May 2020 10:38:03 -0400 Subject: [PATCH] feat(shared): support Map and Set in toDisplayString close #1067, close #1100 --- .../shared/__tests__/toDisplayString.spec.ts | 107 ++++++++++++++++++ packages/shared/src/index.ts | 10 +- packages/shared/src/toDisplayString.ts | 28 +++++ 3 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 packages/shared/__tests__/toDisplayString.spec.ts create mode 100644 packages/shared/src/toDisplayString.ts diff --git a/packages/shared/__tests__/toDisplayString.spec.ts b/packages/shared/__tests__/toDisplayString.spec.ts new file mode 100644 index 00000000000..a998904b1ab --- /dev/null +++ b/packages/shared/__tests__/toDisplayString.spec.ts @@ -0,0 +1,107 @@ +import { toDisplayString } from '../src' + +describe('toDisplayString', () => { + test('nullish values', () => { + expect(toDisplayString(null)).toBe('') + expect(toDisplayString(undefined)).toBe('') + }) + + test('primitive values', () => { + expect(toDisplayString(1)).toBe('1') + expect(toDisplayString(true)).toBe('true') + expect(toDisplayString(false)).toBe('false') + expect(toDisplayString('hello')).toBe('hello') + }) + + test('Object and Arrays', () => { + const obj = { foo: 123 } + expect(toDisplayString(obj)).toBe(JSON.stringify(obj, null, 2)) + const arr = [obj] + expect(toDisplayString(arr)).toBe(JSON.stringify(arr, null, 2)) + }) + + test('native objects', () => { + const div = document.createElement('div') + expect(toDisplayString(div)).toBe(`"[object HTMLDivElement]"`) + expect(toDisplayString({ div })).toMatchInlineSnapshot(` + "{ + \\"div\\": \\"[object HTMLDivElement]\\" + }" + `) + }) + + test('Map and Set', () => { + const m = new Map([ + [1, 'foo'], + [{ baz: 1 }, { foo: 'bar', qux: 2 }] + ]) + const s = new Set([1, { foo: 'bar' }, m]) + + expect(toDisplayString(m)).toMatchInlineSnapshot(` + "{ + \\"Map(2)\\": { + \\"1 =>\\": \\"foo\\", + \\"[object Object] =>\\": { + \\"foo\\": \\"bar\\", + \\"qux\\": 2 + } + } + }" + `) + expect(toDisplayString(s)).toMatchInlineSnapshot(` + "{ + \\"Set(3)\\": [ + 1, + { + \\"foo\\": \\"bar\\" + }, + { + \\"Map(2)\\": { + \\"1 =>\\": \\"foo\\", + \\"[object Object] =>\\": { + \\"foo\\": \\"bar\\", + \\"qux\\": 2 + } + } + } + ] + }" + `) + + expect( + toDisplayString({ + m, + s + }) + ).toMatchInlineSnapshot(` + "{ + \\"m\\": { + \\"Map(2)\\": { + \\"1 =>\\": \\"foo\\", + \\"[object Object] =>\\": { + \\"foo\\": \\"bar\\", + \\"qux\\": 2 + } + } + }, + \\"s\\": { + \\"Set(3)\\": [ + 1, + { + \\"foo\\": \\"bar\\" + }, + { + \\"Map(2)\\": { + \\"1 =>\\": \\"foo\\", + \\"[object Object] =>\\": { + \\"foo\\": \\"bar\\", + \\"qux\\": 2 + } + } + } + ] + } + }" + `) + }) +}) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index c679dfe6d11..066132c8a9a 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -11,6 +11,7 @@ export * from './domTagConfig' export * from './domAttrConfig' export * from './escapeHtml' export * from './looseEqual' +export * from './toDisplayString' export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__ ? Object.freeze({}) @@ -112,15 +113,6 @@ export const capitalize = cacheStringFunction( export const hasChanged = (value: any, oldValue: any): boolean => value !== oldValue && (value === value || oldValue === oldValue) -// For converting {{ interpolation }} values to displayed strings. -export const toDisplayString = (val: unknown): string => { - return val == null - ? '' - : isArray(val) || (isPlainObject(val) && val.toString === objectToString) - ? JSON.stringify(val, null, 2) - : String(val) -} - export const invokeArrayFns = (fns: Function[], arg?: any) => { for (let i = 0; i < fns.length; i++) { fns[i](arg) diff --git a/packages/shared/src/toDisplayString.ts b/packages/shared/src/toDisplayString.ts new file mode 100644 index 00000000000..bd19cdd0779 --- /dev/null +++ b/packages/shared/src/toDisplayString.ts @@ -0,0 +1,28 @@ +import { isArray, isObject, isPlainObject } from './index' + +// For converting {{ interpolation }} values to displayed strings. +export const toDisplayString = (val: unknown): string => { + return val == null + ? '' + : isObject(val) + ? JSON.stringify(val, replacer, 2) + : String(val) +} + +const replacer = (_key: string, val: any) => { + if (val instanceof Map) { + return { + [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => { + ;(entries as any)[`${key} =>`] = val + return entries + }, {}) + } + } else if (val instanceof Set) { + return { + [`Set(${val.size})`]: [...val.values()] + } + } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { + return String(val) + } + return val +}