wip(ssr): restructure
This commit is contained in:
		
							parent
							
								
									d293876c34
								
							
						
					
					
						commit
						012bc5df9d
					
				@ -103,6 +103,7 @@ export { registerRuntimeCompiler } from './component'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// For server-renderer
 | 
					// For server-renderer
 | 
				
			||||||
export { createComponentInstance, setupComponent } from './component'
 | 
					export { createComponentInstance, setupComponent } from './component'
 | 
				
			||||||
 | 
					export { renderComponentRoot } from './componentRenderUtils'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Types -----------------------------------------------------------------------
 | 
					// Types -----------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,9 @@ import {
 | 
				
			|||||||
  isString,
 | 
					  isString,
 | 
				
			||||||
  isObject,
 | 
					  isObject,
 | 
				
			||||||
  EMPTY_ARR,
 | 
					  EMPTY_ARR,
 | 
				
			||||||
  extend
 | 
					  extend,
 | 
				
			||||||
 | 
					  normalizeClass,
 | 
				
			||||||
 | 
					  normalizeStyle
 | 
				
			||||||
} from '@vue/shared'
 | 
					} from '@vue/shared'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ComponentInternalInstance,
 | 
					  ComponentInternalInstance,
 | 
				
			||||||
@ -378,43 +380,6 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
 | 
				
			|||||||
  vnode.shapeFlag |= type
 | 
					  vnode.shapeFlag |= type
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeStyle(
 | 
					 | 
				
			||||||
  value: unknown
 | 
					 | 
				
			||||||
): Record<string, string | number> | void {
 | 
					 | 
				
			||||||
  if (isArray(value)) {
 | 
					 | 
				
			||||||
    const res: Record<string, string | number> = {}
 | 
					 | 
				
			||||||
    for (let i = 0; i < value.length; i++) {
 | 
					 | 
				
			||||||
      const normalized = normalizeStyle(value[i])
 | 
					 | 
				
			||||||
      if (normalized) {
 | 
					 | 
				
			||||||
        for (const key in normalized) {
 | 
					 | 
				
			||||||
          res[key] = normalized[key]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return res
 | 
					 | 
				
			||||||
  } else if (isObject(value)) {
 | 
					 | 
				
			||||||
    return value
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function normalizeClass(value: unknown): string {
 | 
					 | 
				
			||||||
  let res = ''
 | 
					 | 
				
			||||||
  if (isString(value)) {
 | 
					 | 
				
			||||||
    res = value
 | 
					 | 
				
			||||||
  } else if (isArray(value)) {
 | 
					 | 
				
			||||||
    for (let i = 0; i < value.length; i++) {
 | 
					 | 
				
			||||||
      res += normalizeClass(value[i]) + ' '
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } else if (isObject(value)) {
 | 
					 | 
				
			||||||
    for (const name in value) {
 | 
					 | 
				
			||||||
      if (value[name]) {
 | 
					 | 
				
			||||||
        res += name + ' '
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return res.trim()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const handlersRE = /^on|^vnode/
 | 
					const handlersRE = /^on|^vnode/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function mergeProps(...args: (Data & VNodeProps)[]) {
 | 
					export function mergeProps(...args: (Data & VNodeProps)[]) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								packages/server-renderer/__tests__/escape.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/server-renderer/__tests__/escape.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					test('ssr: escape HTML', () => {})
 | 
				
			||||||
							
								
								
									
										17
									
								
								packages/server-renderer/__tests__/renderToString.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/server-renderer/__tests__/renderToString.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					// import { renderToString, renderComponent } from '../src'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('ssr: renderToString', () => {
 | 
				
			||||||
 | 
					  test('basic', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('nested components', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('nested components with optimized slots', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('mixing optimized / vnode components', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('nested components with vnode slots', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('async components', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('parallel async components', () => {})
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										29
									
								
								packages/server-renderer/__tests__/renderVnode.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								packages/server-renderer/__tests__/renderVnode.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					describe('ssr: render raw vnodes', () => {
 | 
				
			||||||
 | 
					  test('class', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('styles', () => {
 | 
				
			||||||
 | 
					    // only render numbers for properties that allow no unit numbers
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('attrs', () => {
 | 
				
			||||||
 | 
					    test('basic', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('boolean attrs', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('enumerated attrs', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('skip falsy values', () => {})
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('domProps', () => {
 | 
				
			||||||
 | 
					    test('innerHTML', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('textContent', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('textarea', () => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('other renderable domProps', () => {
 | 
				
			||||||
 | 
					      // also test camel to kebab case conversion for some props
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@ -1,5 +1,3 @@
 | 
				
			|||||||
import { toDisplayString } from '@vue/shared'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const escapeRE = /["'&<>]/
 | 
					const escapeRE = /["'&<>]/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function escape(string: unknown) {
 | 
					export function escape(string: unknown) {
 | 
				
			||||||
@ -45,7 +43,3 @@ export function escape(string: unknown) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return lastIndex !== index ? html + str.substring(lastIndex, index) : html
 | 
					  return lastIndex !== index ? html + str.substring(lastIndex, index) : html
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function interpolate(value: unknown) {
 | 
					 | 
				
			||||||
  return escape(toDisplayString(value))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,104 +1,16 @@
 | 
				
			|||||||
import {
 | 
					import { toDisplayString } from 'vue'
 | 
				
			||||||
  App,
 | 
					 | 
				
			||||||
  Component,
 | 
					 | 
				
			||||||
  ComponentInternalInstance,
 | 
					 | 
				
			||||||
  createComponentInstance,
 | 
					 | 
				
			||||||
  setupComponent,
 | 
					 | 
				
			||||||
  VNode,
 | 
					 | 
				
			||||||
  createVNode
 | 
					 | 
				
			||||||
} from 'vue'
 | 
					 | 
				
			||||||
import { isString, isPromise, isArray } from '@vue/shared'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export * from './helpers'
 | 
					export { renderToString, renderComponent } from './renderToString'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SSRBuffer = SSRBufferItem[]
 | 
					export {
 | 
				
			||||||
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
 | 
					  renderVNode,
 | 
				
			||||||
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
 | 
					  renderClass,
 | 
				
			||||||
 | 
					  renderStyle,
 | 
				
			||||||
 | 
					  renderProps
 | 
				
			||||||
 | 
					} from './renderVnode'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createBuffer() {
 | 
					export { escape } from './escape'
 | 
				
			||||||
  let appendable = false
 | 
					
 | 
				
			||||||
  let hasAsync = false
 | 
					export function interpolate(value: unknown) {
 | 
				
			||||||
  const buffer: SSRBuffer = []
 | 
					  return escape(toDisplayString(value))
 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    buffer,
 | 
					 | 
				
			||||||
    hasAsync() {
 | 
					 | 
				
			||||||
      return hasAsync
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    push(item: SSRBufferItem) {
 | 
					 | 
				
			||||||
      const isStringItem = isString(item)
 | 
					 | 
				
			||||||
      if (appendable && isStringItem) {
 | 
					 | 
				
			||||||
        buffer[buffer.length - 1] += item as string
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        buffer.push(item)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      appendable = isStringItem
 | 
					 | 
				
			||||||
      if (!isStringItem && !isArray(item)) {
 | 
					 | 
				
			||||||
        // promise
 | 
					 | 
				
			||||||
        hasAsync = true
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function unrollBuffer(buffer: ResolvedSSRBuffer): string {
 | 
					 | 
				
			||||||
  let ret = ''
 | 
					 | 
				
			||||||
  for (let i = 0; i < buffer.length; i++) {
 | 
					 | 
				
			||||||
    const item = buffer[i]
 | 
					 | 
				
			||||||
    if (isString(item)) {
 | 
					 | 
				
			||||||
      ret += item
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      ret += unrollBuffer(item)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return ret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function renderToString(app: App): Promise<string> {
 | 
					 | 
				
			||||||
  const resolvedBuffer = (await renderComponent(
 | 
					 | 
				
			||||||
    app._component,
 | 
					 | 
				
			||||||
    app._props
 | 
					 | 
				
			||||||
  )) as ResolvedSSRBuffer
 | 
					 | 
				
			||||||
  return unrollBuffer(resolvedBuffer)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function renderComponent(
 | 
					 | 
				
			||||||
  comp: Component,
 | 
					 | 
				
			||||||
  props: Record<string, any> | null = null,
 | 
					 | 
				
			||||||
  children: VNode['children'] = null,
 | 
					 | 
				
			||||||
  parentComponent: ComponentInternalInstance | null = null
 | 
					 | 
				
			||||||
): ResolvedSSRBuffer | Promise<SSRBuffer> {
 | 
					 | 
				
			||||||
  const vnode = createVNode(comp, props, children)
 | 
					 | 
				
			||||||
  const instance = createComponentInstance(vnode, parentComponent)
 | 
					 | 
				
			||||||
  const res = setupComponent(instance, null)
 | 
					 | 
				
			||||||
  if (isPromise(res)) {
 | 
					 | 
				
			||||||
    return res.then(() => innerRenderComponent(comp, instance))
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    return innerRenderComponent(comp, instance)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function innerRenderComponent(
 | 
					 | 
				
			||||||
  comp: Component,
 | 
					 | 
				
			||||||
  instance: ComponentInternalInstance
 | 
					 | 
				
			||||||
): ResolvedSSRBuffer | Promise<SSRBuffer> {
 | 
					 | 
				
			||||||
  const { buffer, push, hasAsync } = createBuffer()
 | 
					 | 
				
			||||||
  if (typeof comp === 'function') {
 | 
					 | 
				
			||||||
    // TODO FunctionalComponent
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    if (comp.ssrRender) {
 | 
					 | 
				
			||||||
      // optimized
 | 
					 | 
				
			||||||
      comp.ssrRender(push, instance.proxy)
 | 
					 | 
				
			||||||
    } else if (comp.render) {
 | 
					 | 
				
			||||||
      // TODO fallback to vdom serialization
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      // TODO warn component missing render function
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  // If the current component's buffer contains any Promise from async children,
 | 
					 | 
				
			||||||
  // then it must return a Promise too. Otherwise this is a component that
 | 
					 | 
				
			||||||
  // contains only sync children so we can avoid the async book-keeping overhead.
 | 
					 | 
				
			||||||
  return hasAsync()
 | 
					 | 
				
			||||||
    ? // TS can't figure out the typing due to recursive appearance of Promise
 | 
					 | 
				
			||||||
      Promise.all(buffer as any)
 | 
					 | 
				
			||||||
    : (buffer as ResolvedSSRBuffer)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										109
									
								
								packages/server-renderer/src/renderToString.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								packages/server-renderer/src/renderToString.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  App,
 | 
				
			||||||
 | 
					  Component,
 | 
				
			||||||
 | 
					  ComponentInternalInstance,
 | 
				
			||||||
 | 
					  VNode,
 | 
				
			||||||
 | 
					  createComponentInstance,
 | 
				
			||||||
 | 
					  setupComponent,
 | 
				
			||||||
 | 
					  createVNode,
 | 
				
			||||||
 | 
					  renderComponentRoot
 | 
				
			||||||
 | 
					} from 'vue'
 | 
				
			||||||
 | 
					import { isString, isPromise, isArray, isFunction } from '@vue/shared'
 | 
				
			||||||
 | 
					import { renderVNode } from './renderVnode'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type SSRBuffer = SSRBufferItem[]
 | 
				
			||||||
 | 
					export type SSRBufferItem = string | ResolvedSSRBuffer | Promise<SSRBuffer>
 | 
				
			||||||
 | 
					export type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createBuffer() {
 | 
				
			||||||
 | 
					  let appendable = false
 | 
				
			||||||
 | 
					  let hasAsync = false
 | 
				
			||||||
 | 
					  const buffer: SSRBuffer = []
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    buffer,
 | 
				
			||||||
 | 
					    hasAsync() {
 | 
				
			||||||
 | 
					      return hasAsync
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    push(item: SSRBufferItem) {
 | 
				
			||||||
 | 
					      const isStringItem = isString(item)
 | 
				
			||||||
 | 
					      if (appendable && isStringItem) {
 | 
				
			||||||
 | 
					        buffer[buffer.length - 1] += item as string
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        buffer.push(item)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      appendable = isStringItem
 | 
				
			||||||
 | 
					      if (!isStringItem && !isArray(item)) {
 | 
				
			||||||
 | 
					        // promise
 | 
				
			||||||
 | 
					        hasAsync = true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function unrollBuffer(buffer: ResolvedSSRBuffer): string {
 | 
				
			||||||
 | 
					  let ret = ''
 | 
				
			||||||
 | 
					  for (let i = 0; i < buffer.length; i++) {
 | 
				
			||||||
 | 
					    const item = buffer[i]
 | 
				
			||||||
 | 
					    if (isString(item)) {
 | 
				
			||||||
 | 
					      ret += item
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      ret += unrollBuffer(item)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function renderToString(app: App): Promise<string> {
 | 
				
			||||||
 | 
					  const resolvedBuffer = (await renderComponent(
 | 
				
			||||||
 | 
					    app._component,
 | 
				
			||||||
 | 
					    app._props
 | 
				
			||||||
 | 
					  )) as ResolvedSSRBuffer
 | 
				
			||||||
 | 
					  return unrollBuffer(resolvedBuffer)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderComponent(
 | 
				
			||||||
 | 
					  comp: Component,
 | 
				
			||||||
 | 
					  props: Record<string, any> | null = null,
 | 
				
			||||||
 | 
					  children: VNode['children'] = null,
 | 
				
			||||||
 | 
					  parentComponent: ComponentInternalInstance | null = null
 | 
				
			||||||
 | 
					): ResolvedSSRBuffer | Promise<SSRBuffer> {
 | 
				
			||||||
 | 
					  const vnode = createVNode(comp, props, children)
 | 
				
			||||||
 | 
					  const instance = createComponentInstance(vnode, parentComponent)
 | 
				
			||||||
 | 
					  const res = setupComponent(instance, null)
 | 
				
			||||||
 | 
					  if (isPromise(res)) {
 | 
				
			||||||
 | 
					    return res.then(() => innerRenderComponent(comp, instance))
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return innerRenderComponent(comp, instance)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function innerRenderComponent(
 | 
				
			||||||
 | 
					  comp: Component,
 | 
				
			||||||
 | 
					  instance: ComponentInternalInstance
 | 
				
			||||||
 | 
					): ResolvedSSRBuffer | Promise<SSRBuffer> {
 | 
				
			||||||
 | 
					  const { buffer, push, hasAsync } = createBuffer()
 | 
				
			||||||
 | 
					  if (isFunction(comp)) {
 | 
				
			||||||
 | 
					    renderVNode(push, renderComponentRoot(instance))
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (comp.ssrRender) {
 | 
				
			||||||
 | 
					      // optimized
 | 
				
			||||||
 | 
					      comp.ssrRender(push, instance.proxy)
 | 
				
			||||||
 | 
					    } else if (comp.render) {
 | 
				
			||||||
 | 
					      renderVNode(push, renderComponentRoot(instance))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // TODO on the fly template compilation support
 | 
				
			||||||
 | 
					      throw new Error(
 | 
				
			||||||
 | 
					        `Component ${
 | 
				
			||||||
 | 
					          comp.name ? `${comp.name} ` : ``
 | 
				
			||||||
 | 
					        } is missing render function.`
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // If the current component's buffer contains any Promise from async children,
 | 
				
			||||||
 | 
					  // then it must return a Promise too. Otherwise this is a component that
 | 
				
			||||||
 | 
					  // contains only sync children so we can avoid the async book-keeping overhead.
 | 
				
			||||||
 | 
					  return hasAsync()
 | 
				
			||||||
 | 
					    ? // TS can't figure out the typing due to recursive appearance of Promise
 | 
				
			||||||
 | 
					      Promise.all(buffer as any)
 | 
				
			||||||
 | 
					    : (buffer as ResolvedSSRBuffer)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								packages/server-renderer/src/renderVnode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/server-renderer/src/renderVnode.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					import { VNode } from 'vue'
 | 
				
			||||||
 | 
					import { SSRBufferItem } from './renderToString'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderVNode(
 | 
				
			||||||
 | 
					  push: (item: SSRBufferItem) => void,
 | 
				
			||||||
 | 
					  vnode: VNode
 | 
				
			||||||
 | 
					) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderProps() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderClass() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderStyle() {}
 | 
				
			||||||
@ -6,6 +6,7 @@ export * from './globalsWhitelist'
 | 
				
			|||||||
export * from './codeframe'
 | 
					export * from './codeframe'
 | 
				
			||||||
export * from './domTagConfig'
 | 
					export * from './domTagConfig'
 | 
				
			||||||
export * from './mockWarn'
 | 
					export * from './mockWarn'
 | 
				
			||||||
 | 
					export * from './normalizeProp'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
 | 
					export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
 | 
				
			||||||
  ? Object.freeze({})
 | 
					  ? Object.freeze({})
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										38
									
								
								packages/shared/src/normalizeProp.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								packages/shared/src/normalizeProp.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import { isArray, isString, isObject } from './'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function normalizeStyle(
 | 
				
			||||||
 | 
					  value: unknown
 | 
				
			||||||
 | 
					): Record<string, string | number> | void {
 | 
				
			||||||
 | 
					  if (isArray(value)) {
 | 
				
			||||||
 | 
					    const res: Record<string, string | number> = {}
 | 
				
			||||||
 | 
					    for (let i = 0; i < value.length; i++) {
 | 
				
			||||||
 | 
					      const normalized = normalizeStyle(value[i])
 | 
				
			||||||
 | 
					      if (normalized) {
 | 
				
			||||||
 | 
					        for (const key in normalized) {
 | 
				
			||||||
 | 
					          res[key] = normalized[key]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res
 | 
				
			||||||
 | 
					  } else if (isObject(value)) {
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function normalizeClass(value: unknown): string {
 | 
				
			||||||
 | 
					  let res = ''
 | 
				
			||||||
 | 
					  if (isString(value)) {
 | 
				
			||||||
 | 
					    res = value
 | 
				
			||||||
 | 
					  } else if (isArray(value)) {
 | 
				
			||||||
 | 
					    for (let i = 0; i < value.length; i++) {
 | 
				
			||||||
 | 
					      res += normalizeClass(value[i]) + ' '
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (isObject(value)) {
 | 
				
			||||||
 | 
					    for (const name in value) {
 | 
				
			||||||
 | 
					      if (value[name]) {
 | 
				
			||||||
 | 
					        res += name + ' '
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return res.trim()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user