diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 2123fe82..8d2a8df7 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -51,7 +51,8 @@ export const enum NodeTypes { JS_BLOCK_STATEMENT, JS_TEMPLATE_LITERAL, JS_IF_STATEMENT, - JS_ASSIGNMENT_EXPRESSION + JS_ASSIGNMENT_EXPRESSION, + JS_RETURN_STATEMENT } export const enum ElementTypes { @@ -294,7 +295,7 @@ export interface FunctionExpression extends Node { type: NodeTypes.JS_FUNCTION_EXPRESSION params: ExpressionNode | string | (ExpressionNode | string)[] | undefined returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode - body?: BlockStatement + body?: BlockStatement | IfStatement newline: boolean // so that codegen knows it needs to generate ScopeId wrapper isSlot: boolean @@ -322,7 +323,12 @@ export interface CacheExpression extends Node { // SSR-specific Node Types ----------------------------------------------------- -export type SSRCodegenNode = BlockStatement | TemplateLiteral | IfStatement +export type SSRCodegenNode = + | BlockStatement + | TemplateLiteral + | IfStatement + | AssignmentExpression + | ReturnStatement export interface BlockStatement extends Node { type: NodeTypes.JS_BLOCK_STATEMENT @@ -338,7 +344,7 @@ export interface IfStatement extends Node { type: NodeTypes.JS_IF_STATEMENT test: ExpressionNode consequent: BlockStatement - alternate: IfStatement | BlockStatement | undefined + alternate: IfStatement | BlockStatement | ReturnStatement | undefined } export interface AssignmentExpression extends Node { @@ -347,6 +353,11 @@ export interface AssignmentExpression extends Node { right: JSChildNode } +export interface ReturnStatement extends Node { + type: NodeTypes.JS_RETURN_STATEMENT + returns: TemplateChildNode | TemplateChildNode[] | JSChildNode +} + // Codegen Node Types ---------------------------------------------------------- // createVNode(...) @@ -733,3 +744,13 @@ export function createAssignmentExpression( loc: locStub } } + +export function createReturnStatement( + returns: ReturnStatement['returns'] +): ReturnStatement { + return { + type: NodeTypes.JS_RETURN_STATEMENT, + returns, + loc: locStub + } +} diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index cc0637fc..ecebf513 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -22,7 +22,8 @@ import { SSRCodegenNode, TemplateLiteral, IfStatement, - AssignmentExpression + AssignmentExpression, + ReturnStatement } from './ast' import { SourceMapGenerator, RawSourceMap } from 'source-map' import { @@ -44,8 +45,7 @@ import { CREATE_TEXT, PUSH_SCOPE_ID, POP_SCOPE_ID, - WITH_SCOPE_ID, - CREATE_BLOCK + WITH_SCOPE_ID } from './runtimeHelpers' import { ImportItem } from './transform' @@ -334,24 +334,12 @@ function genModulePreamble( context: CodegenContext, genScopeId: boolean ) { - const { push, helper, newline, scopeId, runtimeModuleName, ssr } = context + const { push, helper, newline, scopeId, runtimeModuleName } = context - if (!__BROWSER__) { - // in ssr mode, `withId` helper is only needed if the template contains - // de-optimized component slots (which uses the createVNode helper) - if ( - ssr && - !( - ast.helpers.includes(CREATE_VNODE) || ast.helpers.includes(CREATE_BLOCK) - ) - ) { - genScopeId = false - } - if (genScopeId) { - ast.helpers.push(WITH_SCOPE_ID) - if (ast.hoists.length) { - ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID) - } + if (!__BROWSER__ && genScopeId) { + ast.helpers.push(WITH_SCOPE_ID) + if (ast.hoists.length) { + ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID) } } @@ -572,6 +560,9 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) { case NodeTypes.JS_ASSIGNMENT_EXPRESSION: !__BROWSER__ && genAssignmentExpression(node, context) break + case NodeTypes.JS_RETURN_STATEMENT: + !__BROWSER__ && genReturnStatement(node, context) + break /* istanbul ignore next */ default: @@ -851,3 +842,15 @@ function genAssignmentExpression( context.push(` = `) genNode(node.right, context) } + +function genReturnStatement( + { returns }: ReturnStatement, + context: CodegenContext +) { + context.push(`return `) + if (isArray(returns)) { + genNodeListAsArray(returns, context) + } else { + genNode(returns, context) + } +} diff --git a/packages/compiler-core/src/compile.ts b/packages/compiler-core/src/compile.ts index 7699dae8..f7717228 100644 --- a/packages/compiler-core/src/compile.ts +++ b/packages/compiler-core/src/compile.ts @@ -1,6 +1,6 @@ import { CompilerOptions } from './options' import { baseParse } from './parse' -import { transform } from './transform' +import { transform, NodeTransform, DirectiveTransform } from './transform' import { generate, CodegenResult } from './codegen' import { RootNode } from './ast' import { isString } from '@vue/shared' @@ -17,6 +17,39 @@ import { transformOnce } from './transforms/vOnce' import { transformModel } from './transforms/vModel' import { defaultOnError, createCompilerError, ErrorCodes } from './errors' +export type TransformPreset = [ + NodeTransform[], + Record +] + +export function getBaseTransformPreset( + prefixIdentifiers?: boolean +): TransformPreset { + return [ + [ + transformOnce, + transformIf, + transformFor, + ...(!__BROWSER__ && prefixIdentifiers + ? [ + // order is important + trackVForSlotScopes, + transformExpression + ] + : []), + transformSlotOutlet, + transformElement, + trackSlotScopes, + transformText + ], + { + on: transformOn, + bind: transformBind, + model: transformModel + } + ] +} + // we name it `baseCompile` so that higher order compilers like // @vue/compiler-dom can export `compile` while re-exporting everything else. export function baseCompile( @@ -44,30 +77,18 @@ export function baseCompile( } const ast = isString(template) ? baseParse(template, options) : template + const [nodeTransforms, directiveTransforms] = getBaseTransformPreset( + prefixIdentifiers + ) transform(ast, { ...options, prefixIdentifiers, nodeTransforms: [ - transformOnce, - transformIf, - transformFor, - ...(prefixIdentifiers - ? [ - // order is important - trackVForSlotScopes, - transformExpression - ] - : []), - transformSlotOutlet, - transformElement, - trackSlotScopes, - transformText, + ...nodeTransforms, ...(options.nodeTransforms || []) // user transforms ], directiveTransforms: { - on: transformOn, - bind: transformBind, - model: transformModel, + ...directiveTransforms, ...(options.directiveTransforms || {}) // user transforms } }) diff --git a/packages/compiler-core/src/index.ts b/packages/compiler-core/src/index.ts index 56c908fe..945d0f06 100644 --- a/packages/compiler-core/src/index.ts +++ b/packages/compiler-core/src/index.ts @@ -10,8 +10,8 @@ export { export { baseParse, TextModes } from './parse' export { transform, - createStructuralDirectiveTransform, TransformContext, + createStructuralDirectiveTransform, NodeTransform, StructuralDirectiveTransform, DirectiveTransform @@ -23,18 +23,16 @@ export { CompilerError, createCompilerError } from './errors' + export * from './ast' export * from './utils' -export { registerRuntimeHelpers } from './runtimeHelpers' -export { noopDirectiveTransform } from './transforms/noopDirectiveTransform' +export * from './runtimeHelpers' -// expose transforms so higher-order compilers can import and extend them +export { getBaseTransformPreset, TransformPreset } from './compile' export { transformModel } from './transforms/vModel' export { transformOn } from './transforms/vOn' export { transformBind } from './transforms/vBind' - -// exported for compiler-ssr -export * from './runtimeHelpers' +export { noopDirectiveTransform } from './transforms/noopDirectiveTransform' export { processIf } from './transforms/vIf' export { processFor, createForLoopParams } from './transforms/vFor' export { diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 578e08d8..1020481b 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -85,8 +85,8 @@ export interface TransformContext extends Required { components: Set directives: Set hoists: JSChildNode[] - temps: number imports: Set + temps: number cached: number identifiers: { [name: string]: number | undefined } scopes: { @@ -141,8 +141,8 @@ function createTransformContext( components: new Set(), directives: new Set(), hoists: [], - temps: 0, imports: new Set(), + temps: 0, cached: 0, identifiers: {}, scopes: { diff --git a/packages/compiler-core/src/transforms/transformText.ts b/packages/compiler-core/src/transforms/transformText.ts index a15a2fd1..c60e86dc 100644 --- a/packages/compiler-core/src/transforms/transformText.ts +++ b/packages/compiler-core/src/transforms/transformText.ts @@ -77,7 +77,7 @@ export const transformText: NodeTransform = (node, context) => { callArgs.push(child) } // mark dynamic text with flag so it gets patched inside a block - if (child.type !== NodeTypes.TEXT) { + if (!context.ssr && child.type !== NodeTypes.TEXT) { callArgs.push( `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */` ) diff --git a/packages/compiler-dom/src/index.ts b/packages/compiler-dom/src/index.ts index 674a3ef8..60989f14 100644 --- a/packages/compiler-dom/src/index.ts +++ b/packages/compiler-dom/src/index.ts @@ -6,7 +6,9 @@ import { isBuiltInType, ParserOptions, RootNode, - noopDirectiveTransform + noopDirectiveTransform, + TransformPreset, + getBaseTransformPreset } from '@vue/compiler-core' import { parserOptionsMinimal } from './parserOptionsMinimal' import { parserOptionsStandard } from './parserOptionsStandard' @@ -31,25 +33,43 @@ export const isBuiltInDOMComponent = (tag: string): symbol | undefined => { } } -export function compile( - template: string, - options: CompilerOptions = {} -): CodegenResult { - return baseCompile(template, { - ...parserOptions, - ...options, - nodeTransforms: [ +export function getDOMTransformPreset( + prefixIdentifiers?: boolean +): TransformPreset { + const [nodeTransforms, directiveTransforms] = getBaseTransformPreset( + prefixIdentifiers + ) + return [ + [ + ...nodeTransforms, transformStyle, - ...(__DEV__ ? [warnTransitionChildren] : []), - ...(options.nodeTransforms || []) + ...(__DEV__ ? [warnTransitionChildren] : []) ], - directiveTransforms: { + { + ...directiveTransforms, cloak: noopDirectiveTransform, html: transformVHtml, text: transformVText, model: transformModel, // override compiler-core - on: transformOn, - show: transformShow, + on: transformOn, // override compiler-core + show: transformShow + } + ] +} + +export function compile( + template: string, + options: CompilerOptions = {} +): CodegenResult { + const [nodeTransforms, directiveTransforms] = getDOMTransformPreset( + options.prefixIdentifiers + ) + return baseCompile(template, { + ...parserOptions, + ...options, + nodeTransforms: [...nodeTransforms, ...(options.nodeTransforms || [])], + directiveTransforms: { + ...directiveTransforms, ...(options.directiveTransforms || {}) }, isBuiltInComponent: isBuiltInDOMComponent diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index 8611a38f..c70654bf 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -57,10 +57,13 @@ describe('ssr: components', () => { _push(_ssrRenderComponent(_component_foo, null, { default: (_, _push, _parent, _scopeId) => { - if (_scopeId) { - _push(\`hello
\`) + if (_push) { + _push(\`hello\`) } else { - _push(\`hello
\`) + return [ + createTextVNode(\\"hello\\"), + createVNode(\\"div\\") + ] } }, _compiled: true @@ -80,7 +83,13 @@ describe('ssr: components', () => { _push(_ssrRenderComponent(_component_foo, null, { default: ({ msg }, _push, _parent, _scopeId) => { - _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`) + if (_push) { + _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`) + } else { + return [ + createTextVNode(toDisplayString(_ctx.msg + _ctx.outer)) + ] + } }, _compiled: true }, _parent)) @@ -103,10 +112,22 @@ describe('ssr: components', () => { _push(_ssrRenderComponent(_component_foo, null, { default: (_, _push, _parent, _scopeId) => { - _push(\`foo\`) + if (_push) { + _push(\`foo\`) + } else { + return [ + createTextVNode(\\"foo\\") + ] + } }, named: (_, _push, _parent, _scopeId) => { - _push(\`bar\`) + if (_push) { + _push(\`bar\`) + } else { + return [ + createTextVNode(\\"bar\\") + ] + } }, _compiled: true }, _parent)) @@ -131,7 +152,13 @@ describe('ssr: components', () => { ? { name: \\"named\\", fn: (_, _push, _parent, _scopeId) => { - _push(\`foo\`) + if (_push) { + _push(\`foo\`) + } else { + return [ + createTextVNode(\\"foo\\") + ] + } } } : undefined diff --git a/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts b/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts index 31b1ef28..ecb492fc 100644 --- a/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts @@ -31,7 +31,13 @@ describe('ssr: scopeId', () => { _push(_ssrRenderComponent(_component_foo, null, { default: (_, _push, _parent, _scopeId) => { - _push(\`foo\`) + if (_push) { + _push(\`foo\`) + } else { + return [ + createTextVNode(\\"foo\\") + ] + } }, _compiled: true }, _parent)) @@ -53,10 +59,12 @@ describe('ssr: scopeId', () => { _push(_ssrRenderComponent(_component_foo, null, { default: (_, _push, _parent, _scopeId) => { - if (_scopeId) { - _push(\`hello\`) + if (_push) { + _push(\`hello\`) } else { - _push(\`hello\`) + return [ + createVNode(\\"span\\", null, \\"hello\\") + ] } }, _compiled: true @@ -80,30 +88,30 @@ describe('ssr: scopeId', () => { _push(_ssrRenderComponent(_component_foo, null, { default: (_, _push, _parent, _scopeId) => { - if (_scopeId) { - _push(\`hello\`) + if (_push) { + _push(\`hello\`) _push(_ssrRenderComponent(_component_bar, null, { default: (_, _push, _parent, _scopeId) => { - if (_scopeId) { - _push(\`\`) + if (_push) { + _push(\`\`) } else { - _push(\`\`) + return [ + createVNode(\\"span\\") + ] } }, _compiled: true }, _parent)) } else { - _push(\`hello\`) - _push(_ssrRenderComponent(_component_bar, null, { - default: (_, _push, _parent, _scopeId) => { - if (_scopeId) { - _push(\`\`) - } else { - _push(\`\`) - } - }, - _compiled: true - }, _parent)) + return [ + createVNode(\\"span\\", null, \\"hello\\"), + createVNode(_component_bar, null, { + default: () => [ + createVNode(\\"span\\") + ], + _compiled: true + }) + ] } }, _compiled: true diff --git a/packages/compiler-ssr/src/ssrCodegenTransform.ts b/packages/compiler-ssr/src/ssrCodegenTransform.ts index c5012e86..b45f0a95 100644 --- a/packages/compiler-ssr/src/ssrCodegenTransform.ts +++ b/packages/compiler-ssr/src/ssrCodegenTransform.ts @@ -28,7 +28,7 @@ import { ssrProcessElement } from './transforms/ssrTransformElement' // passing it to codegen. export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) { - const context = createSSRTransformContext(options) + const context = createSSRTransformContext(ast, options) const isFragment = ast.children.length > 1 && ast.children.some(c => !isText(c)) processChildren(ast.children, context, isFragment) @@ -46,6 +46,7 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) { export type SSRTransformContext = ReturnType function createSSRTransformContext( + root: RootNode, options: CompilerOptions, helpers: Set = new Set(), withSlotScopeId = false @@ -54,6 +55,7 @@ function createSSRTransformContext( let currentString: TemplateLiteral | null = null return { + root, options, body, helpers, @@ -91,6 +93,7 @@ function createChildContext( ): SSRTransformContext { // ensure child inherits parent helpers return createSSRTransformContext( + parent.root, parent.options, parent.helpers, withSlotScopeId diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index 2ee5fd33..0d540392 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -8,7 +8,6 @@ import { ComponentNode, SlotFnBuilder, createFunctionExpression, - createBlockStatement, buildSlots, FunctionExpression, TemplateChildNode, @@ -17,7 +16,14 @@ import { TRANSITION_GROUP, createIfStatement, createSimpleExpression, - isText + getDOMTransformPreset, + transform, + createReturnStatement, + ReturnStatement, + Namespaces, + locStub, + RootNode, + TransformContext } from '@vue/compiler-dom' import { SSR_RENDER_COMPONENT } from '../runtimeHelpers' import { @@ -25,7 +31,7 @@ import { processChildren, processChildrenAsStatement } from '../ssrCodegenTransform' -import { isSymbol } from '@vue/shared' +import { isSymbol, isObject, isArray } from '@vue/shared' // We need to construct the slot functions in the 1st pass to ensure proper // scope tracking, but the children of each slot cannot be processed until @@ -36,6 +42,7 @@ const wipMap = new WeakMap() interface WIPSlotEntry { fn: FunctionExpression children: TemplateChildNode[] + vnodeBranch: ReturnStatement } const componentTypeMap = new WeakMap() @@ -55,26 +62,32 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { return // built-in component: fallthrough } - // note we are not passing ssr: true here because for components, v-on - // handlers should still be passed const props = - node.props.length > 0 ? buildProps(node, context).props || `null` : `null` + node.props.length > 0 + ? // note we are not passing ssr: true here because for components, v-on + // handlers should still be passed + buildProps(node, context).props || `null` + : `null` const wipEntries: WIPSlotEntry[] = [] wipMap.set(node, wipEntries) const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => { - // An SSR slot function has the signature of - // (props, _push, _parent, _scopeId) => void - // See server-renderer/src/helpers/renderSlot.ts const fn = createFunctionExpression( [props || `_`, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later true, // newline - false, // isSlot: pass false since we don't need client scopeId codegen + true, // isSlot loc ) - wipEntries.push({ fn, children }) + wipEntries.push({ + fn, + children, + // build the children using normal vnode-based transforms + // TODO fixme: `children` here has already been mutated at this point + // so the sub-transform runs into errors :/ + vnodeBranch: createVNodeSlotBranch(clone(children), context) + }) return fn } @@ -82,9 +95,6 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { ? buildSlots(node, context, buildSSRSlotFn).slots : `null` - // TODO option for slots bail out - // TODO scopeId - node.ssrCodegenNode = createCallExpression( context.helper(SSR_RENDER_COMPONENT), [component, props, slots, `_parent`] @@ -113,26 +123,79 @@ export function ssrProcessComponent( // finish up slot function expressions from the 1st pass. const wipEntries = wipMap.get(node) || [] for (let i = 0; i < wipEntries.length; i++) { - const { fn, children } = wipEntries[i] - const hasNonTextChild = children.some(c => !isText(c)) - if (hasNonTextChild) { - // SSR slots need to handled potential presence of scopeId of the child - // component. To avoid the cost of concatenation when it's unnecessary, - // we split the code into two paths, one with slot scopeId and one without. - fn.body = createBlockStatement([ - createIfStatement( - createSimpleExpression(`_scopeId`, false), - // branch with scopeId concatenation - processChildrenAsStatement(children, context, false, true), - // branch without scopeId concatenation - processChildrenAsStatement(children, context, false, false) - ) - ]) - } else { - // only text, no need for scopeId branching. - fn.body = processChildrenAsStatement(children, context) - } + const { fn, children, vnodeBranch } = wipEntries[i] + // For each slot, we generate two branches: one SSR-optimized branch and + // one normal vnode-based branch. The branches are taken based on the + // presence of the 2nd `_push` argument (which is only present if the slot + // is called by `_ssrRenderSlot`. + fn.body = createIfStatement( + createSimpleExpression(`_push`, false), + processChildrenAsStatement( + children, + context, + false, + true /* withSlotScopeId */ + ), + vnodeBranch + ) } context.pushStatement(createCallExpression(`_push`, [node.ssrCodegenNode])) } } + +function createVNodeSlotBranch( + children: TemplateChildNode[], + context: TransformContext +): ReturnStatement { + // we need to process the slot children using client-side transforms. + // in order to do that we need to construct a fresh root. + // in addition, wrap the children with a wrapper template for proper child + // treatment. + const { root } = context + const childRoot: RootNode = { + ...root, + children: [ + { + type: NodeTypes.ELEMENT, + ns: Namespaces.HTML, + tag: 'template', + tagType: ElementTypes.TEMPLATE, + isSelfClosing: false, + props: [], + children, + loc: locStub, + codegenNode: undefined + } + ] + } + const [nodeTransforms, directiveTransforms] = getDOMTransformPreset(true) + transform(childRoot, { + ...context, // copy transform options on context + nodeTransforms, + directiveTransforms + }) + + // merge helpers/components/directives/imports from the childRoot + // back to current root + ;(['helpers', 'components', 'directives', 'imports'] as const).forEach( + key => { + root[key] = [...new Set([...root[key], ...childRoot[key]])] as any + } + ) + + return createReturnStatement(children) +} + +function clone(v: any): any { + if (isArray(v)) { + return v.map(clone) + } else if (isObject(v)) { + const res: any = {} + for (const key in v) { + res[key] = v[key] + } + return res + } else { + return v + } +} diff --git a/packages/compiler-ssr/src/transforms/ssrTransformElement.ts b/packages/compiler-ssr/src/transforms/ssrTransformElement.ts index 000266b0..6354fd79 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformElement.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformElement.ts @@ -309,7 +309,6 @@ export function ssrProcessElement( // Handle slot scopeId if (context.withSlotScopeId) { - context.pushStringPart(` `) context.pushStringPart(createSimpleExpression(`_scopeId`, false)) } diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 3200dd3d..32d89268 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -8,6 +8,7 @@ import { VNode } from '../vnode' import { PatchFlags } from '@vue/shared' +import { warn } from '../warning' export function renderSlot( slots: Record, @@ -17,7 +18,17 @@ export function renderSlot( // the compiler and guaranteed to be an array fallback?: VNodeArrayChildren ): VNode { - const slot = slots[name] + let slot = slots[name] + + if (__DEV__ && slot.length > 1) { + warn( + `SSR-optimized slot function detected in a non-SSR-optimized render ` + + `function. You need to mark this component with $dynamic-slots in the ` + + `parent template.` + ) + slot = () => [] + } + return ( openBlock(), createBlock( diff --git a/packages/server-renderer/src/helpers/ssrRenderSlot.ts b/packages/server-renderer/src/helpers/ssrRenderSlot.ts index 0ffc8176..3caaf442 100644 --- a/packages/server-renderer/src/helpers/ssrRenderSlot.ts +++ b/packages/server-renderer/src/helpers/ssrRenderSlot.ts @@ -25,7 +25,7 @@ export function ssrRenderSlot( if (slotFn.length > 1) { // only ssr-optimized slot fns accept more than 1 arguments const scopeId = parentComponent && parentComponent.type.__scopeId - slotFn(slotProps, push, parentComponent, scopeId ? scopeId + `-s` : null) + slotFn(slotProps, push, parentComponent, scopeId ? ` ${scopeId}-s` : ``) } else { // normal slot renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent) diff --git a/packages/template-explorer/src/index.ts b/packages/template-explorer/src/index.ts index b774a779..cf97e8e6 100644 --- a/packages/template-explorer/src/index.ts +++ b/packages/template-explorer/src/index.ts @@ -30,7 +30,7 @@ window.init = () => { ssrMode.value = persistedState.ssr Object.assign(compilerOptions, persistedState.options) - let lastSuccessfulCode: string = `/* See console for error */` + let lastSuccessfulCode: string let lastSuccessfulMap: SourceMapConsumer | undefined = undefined function compileCode(source: string): string { console.clear() @@ -57,6 +57,9 @@ window.init = () => { lastSuccessfulMap = new window._deps['source-map'].SourceMapConsumer(map) lastSuccessfulMap!.computeColumnSpans() } catch (e) { + lastSuccessfulCode = `/* ERROR: ${ + e.message + } (see console for more info) */` console.error(e) } return lastSuccessfulCode