diff --git a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts index 8a439dbf4b5..8afdda39b3d 100644 --- a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts @@ -292,6 +292,133 @@ describe('ssr: v-model', () => { _push(\`\`) }" `) + + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`
\`) + }" + `) + + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper(``) + .code, + ).toMatchInlineSnapshot(` + "const { ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) }) test('', () => { diff --git a/packages/compiler-ssr/src/transforms/ssrVModel.ts b/packages/compiler-ssr/src/transforms/ssrVModel.ts index cbe5b2b42a3..5213c199500 100644 --- a/packages/compiler-ssr/src/transforms/ssrVModel.ts +++ b/packages/compiler-ssr/src/transforms/ssrVModel.ts @@ -6,12 +6,16 @@ import { NodeTypes, type PlainElementNode, type TemplateChildNode, + type TemplateLiteral, + type TextNode, createCallExpression, createConditionalExpression, createDOMCompilerError, createInterpolation, createObjectProperty, createSimpleExpression, + createTemplateLiteral, + findDir, findProp, hasDynamicKeyVBind, transformModel, @@ -54,21 +58,26 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { function processOption(plainNode: PlainElementNode) { if (plainNode.tag === 'option') { if (plainNode.props.findIndex(p => p.name === 'selected') === -1) { - const value = findValueBinding(plainNode) + const value = findOptionValue(plainNode) plainNode.ssrCodegenNode!.elements.push( createConditionalExpression( createCallExpression(context.helper(SSR_INCLUDE_BOOLEAN_ATTR), [ - createConditionalExpression( - createCallExpression(`Array.isArray`, [model]), - createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [ - model, - value, - ]), - createCallExpression(context.helper(SSR_LOOSE_EQUAL), [ - model, - value, - ]), - ), + value.maybeArray + ? createConditionalExpression( + createCallExpression(`Array.isArray`, [model]), + createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [ + model, + value.node, + ]), + createCallExpression(context.helper(SSR_LOOSE_EQUAL), [ + model, + value.node, + ]), + ) + : createCallExpression(context.helper(SSR_LOOSE_EQUAL), [ + model, + value.node, + ]), ]), createSimpleExpression(' selected', true), createSimpleExpression('', true), @@ -190,6 +199,65 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { } } +interface OptionValue { + node: ExpressionNode | TemplateLiteral + maybeArray: boolean +} + +function findOptionValue(node: PlainElementNode): OptionValue { + const valueBinding = findProp(node, 'value') + if (valueBinding) { + return { + node: + valueBinding.type === NodeTypes.DIRECTIVE + ? valueBinding.exp! + : createSimpleExpression(valueBinding.value!.content, true), + maybeArray: true, + } + } + + const textDir = findDir(node, 'text') + if (textDir) { + return { node: textDir.exp!, maybeArray: false } + } + + if ( + node.children.every( + x => + x.type === NodeTypes.TEXT || + x.type === NodeTypes.COMMENT || + x.type === NodeTypes.INTERPOLATION, + ) + ) { + const relevantNodes = collapseTextBetweenComments(node.children).filter( + x => x.type !== NodeTypes.COMMENT, + ) + if (relevantNodes.length) { + const textContentValue = createTemplateLiteral( + relevantNodes.map((x, i) => { + if (x.type === NodeTypes.TEXT) { + let content = x.content + if (i === 0) { + content = content.trimStart() + } + if (i === relevantNodes.length - 1) { + content = content.trimEnd() + } + return createSimpleExpression(content, true) + } else { + return x.content + } + }), + ) + if (textContentValue) { + return { node: textContentValue, maybeArray: false } + } + } + } + + return { node: createSimpleExpression(``, true), maybeArray: false } +} + function findValueBinding(node: PlainElementNode): ExpressionNode { const valueBinding = findProp(node, 'value') return valueBinding @@ -198,3 +266,41 @@ function findValueBinding(node: PlainElementNode): ExpressionNode { : createSimpleExpression(valueBinding.value!.content, true) : createSimpleExpression(`null`, false) } + +function collapseTextBetweenComments