|
@@ -10,7 +10,6 @@ import {
|
|
|
Fragment,
|
|
Fragment,
|
|
|
ssrUtils,
|
|
ssrUtils,
|
|
|
Slots,
|
|
Slots,
|
|
|
- warn,
|
|
|
|
|
createApp,
|
|
createApp,
|
|
|
ssrContextKey
|
|
ssrContextKey
|
|
|
} from 'vue'
|
|
} from 'vue'
|
|
@@ -139,6 +138,8 @@ export function renderComponent(
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export const AsyncSetupErrorMarker = Symbol('Vue async setup error')
|
|
|
|
|
+
|
|
|
function renderComponentVNode(
|
|
function renderComponentVNode(
|
|
|
vnode: VNode,
|
|
vnode: VNode,
|
|
|
parentComponent: ComponentInternalInstance | null = null
|
|
parentComponent: ComponentInternalInstance | null = null
|
|
@@ -150,7 +151,21 @@ function renderComponentVNode(
|
|
|
true /* isSSR */
|
|
true /* isSSR */
|
|
|
)
|
|
)
|
|
|
if (isPromise(res)) {
|
|
if (isPromise(res)) {
|
|
|
- return res.then(() => renderComponentSubTree(instance))
|
|
|
|
|
|
|
+ return res
|
|
|
|
|
+ .catch(err => {
|
|
|
|
|
+ // normalize async setup rejection
|
|
|
|
|
+ if (!(err instanceof Error)) {
|
|
|
|
|
+ err = new Error(String(err))
|
|
|
|
|
+ }
|
|
|
|
|
+ err[AsyncSetupErrorMarker] = true
|
|
|
|
|
+ console.error(
|
|
|
|
|
+ `[@vue/server-renderer]: Uncaught error in async setup:\n`,
|
|
|
|
|
+ err
|
|
|
|
|
+ )
|
|
|
|
|
+ // rethrow for suspense
|
|
|
|
|
+ throw err
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => renderComponentSubTree(instance))
|
|
|
} else {
|
|
} else {
|
|
|
return renderComponentSubTree(instance)
|
|
return renderComponentSubTree(instance)
|
|
|
}
|
|
}
|
|
@@ -208,7 +223,9 @@ function ssrCompile(
|
|
|
isNativeTag: instance.appContext.config.isNativeTag || NO,
|
|
isNativeTag: instance.appContext.config.isNativeTag || NO,
|
|
|
onError(err: CompilerError) {
|
|
onError(err: CompilerError) {
|
|
|
if (__DEV__) {
|
|
if (__DEV__) {
|
|
|
- const message = `Template compilation error: ${err.message}`
|
|
|
|
|
|
|
+ const message = `[@vue/server-renderer] Template compilation error: ${
|
|
|
|
|
+ err.message
|
|
|
|
|
+ }`
|
|
|
const codeFrame =
|
|
const codeFrame =
|
|
|
err.loc &&
|
|
err.loc &&
|
|
|
generateCodeFrame(
|
|
generateCodeFrame(
|
|
@@ -216,7 +233,7 @@ function ssrCompile(
|
|
|
err.loc.start.offset,
|
|
err.loc.start.offset,
|
|
|
err.loc.end.offset
|
|
err.loc.end.offset
|
|
|
)
|
|
)
|
|
|
- warn(codeFrame ? `${message}\n${codeFrame}` : message)
|
|
|
|
|
|
|
+ console.error(codeFrame ? `${message}\n${codeFrame}` : message)
|
|
|
} else {
|
|
} else {
|
|
|
throw err
|
|
throw err
|
|
|
}
|
|
}
|
|
@@ -243,15 +260,15 @@ function renderVNode(
|
|
|
break
|
|
break
|
|
|
default:
|
|
default:
|
|
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
|
|
- renderElement(push, vnode, parentComponent)
|
|
|
|
|
|
|
+ renderElementVNode(push, vnode, parentComponent)
|
|
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
|
|
push(renderComponentVNode(vnode, parentComponent))
|
|
push(renderComponentVNode(vnode, parentComponent))
|
|
|
} else if (shapeFlag & ShapeFlags.PORTAL) {
|
|
} else if (shapeFlag & ShapeFlags.PORTAL) {
|
|
|
- renderPortal(vnode, parentComponent)
|
|
|
|
|
|
|
+ renderPortalVNode(vnode, parentComponent)
|
|
|
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
|
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
|
|
- push(renderSuspense(vnode, parentComponent))
|
|
|
|
|
|
|
+ push(renderSuspenseVNode(vnode, parentComponent))
|
|
|
} else {
|
|
} else {
|
|
|
- console.warn(
|
|
|
|
|
|
|
+ console.error(
|
|
|
'[@vue/server-renderer] Invalid VNode type:',
|
|
'[@vue/server-renderer] Invalid VNode type:',
|
|
|
type,
|
|
type,
|
|
|
`(${typeof type})`
|
|
`(${typeof type})`
|
|
@@ -270,7 +287,7 @@ export function renderVNodeChildren(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function renderElement(
|
|
|
|
|
|
|
+function renderElementVNode(
|
|
|
push: PushFn,
|
|
push: PushFn,
|
|
|
vnode: VNode,
|
|
vnode: VNode,
|
|
|
parentComponent: ComponentInternalInstance
|
|
parentComponent: ComponentInternalInstance
|
|
@@ -325,17 +342,17 @@ function renderElement(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function renderPortal(
|
|
|
|
|
|
|
+function renderPortalVNode(
|
|
|
vnode: VNode,
|
|
vnode: VNode,
|
|
|
parentComponent: ComponentInternalInstance
|
|
parentComponent: ComponentInternalInstance
|
|
|
) {
|
|
) {
|
|
|
const target = vnode.props && vnode.props.target
|
|
const target = vnode.props && vnode.props.target
|
|
|
if (!target) {
|
|
if (!target) {
|
|
|
- console.warn(`[@vue/server-renderer] Portal is missing target prop.`)
|
|
|
|
|
|
|
+ console.error(`[@vue/server-renderer] Portal is missing target prop.`)
|
|
|
return []
|
|
return []
|
|
|
}
|
|
}
|
|
|
if (!isString(target)) {
|
|
if (!isString(target)) {
|
|
|
- console.warn(
|
|
|
|
|
|
|
+ console.error(
|
|
|
`[@vue/server-renderer] Portal target must be a query selector string.`
|
|
`[@vue/server-renderer] Portal target must be a query selector string.`
|
|
|
)
|
|
)
|
|
|
return []
|
|
return []
|
|
@@ -367,7 +384,7 @@ async function resolvePortals(context: SSRContext) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function renderSuspense(
|
|
|
|
|
|
|
+async function renderSuspenseVNode(
|
|
|
vnode: VNode,
|
|
vnode: VNode,
|
|
|
parentComponent: ComponentInternalInstance
|
|
parentComponent: ComponentInternalInstance
|
|
|
): Promise<ResolvedSSRBuffer> {
|
|
): Promise<ResolvedSSRBuffer> {
|
|
@@ -375,10 +392,15 @@ async function renderSuspense(
|
|
|
try {
|
|
try {
|
|
|
const { push, getBuffer } = createBuffer()
|
|
const { push, getBuffer } = createBuffer()
|
|
|
renderVNode(push, content, parentComponent)
|
|
renderVNode(push, content, parentComponent)
|
|
|
|
|
+ // await here so error can be caught
|
|
|
return await getBuffer()
|
|
return await getBuffer()
|
|
|
- } catch {
|
|
|
|
|
- const { push, getBuffer } = createBuffer()
|
|
|
|
|
- renderVNode(push, fallback, parentComponent)
|
|
|
|
|
- return getBuffer()
|
|
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ if (e[AsyncSetupErrorMarker]) {
|
|
|
|
|
+ const { push, getBuffer } = createBuffer()
|
|
|
|
|
+ renderVNode(push, fallback, parentComponent)
|
|
|
|
|
+ return getBuffer()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw e
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|