'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var compilerDom = require('@vue/compiler-dom'); var shared = require('@vue/shared'); const SSR_INTERPOLATE = Symbol(`ssrInterpolate`); const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`); const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`); const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`); const SSR_RENDER_SLOT_INNER = Symbol(`ssrRenderSlotInner`); const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`); const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`); const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`); const SSR_RENDER_ATTR = Symbol(`ssrRenderAttr`); const SSR_RENDER_DYNAMIC_ATTR = Symbol(`ssrRenderDynamicAttr`); const SSR_RENDER_LIST = Symbol(`ssrRenderList`); const SSR_INCLUDE_BOOLEAN_ATTR = Symbol(`ssrIncludeBooleanAttr`); const SSR_LOOSE_EQUAL = Symbol(`ssrLooseEqual`); const SSR_LOOSE_CONTAIN = Symbol(`ssrLooseContain`); const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`); const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`); const SSR_RENDER_TELEPORT = Symbol(`ssrRenderTeleport`); const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`); const SSR_GET_DIRECTIVE_PROPS = Symbol(`ssrGetDirectiveProps`); const ssrHelpers = { [SSR_INTERPOLATE]: `ssrInterpolate`, [SSR_RENDER_VNODE]: `ssrRenderVNode`, [SSR_RENDER_COMPONENT]: `ssrRenderComponent`, [SSR_RENDER_SLOT]: `ssrRenderSlot`, [SSR_RENDER_SLOT_INNER]: `ssrRenderSlotInner`, [SSR_RENDER_CLASS]: `ssrRenderClass`, [SSR_RENDER_STYLE]: `ssrRenderStyle`, [SSR_RENDER_ATTRS]: `ssrRenderAttrs`, [SSR_RENDER_ATTR]: `ssrRenderAttr`, [SSR_RENDER_DYNAMIC_ATTR]: `ssrRenderDynamicAttr`, [SSR_RENDER_LIST]: `ssrRenderList`, [SSR_INCLUDE_BOOLEAN_ATTR]: `ssrIncludeBooleanAttr`, [SSR_LOOSE_EQUAL]: `ssrLooseEqual`, [SSR_LOOSE_CONTAIN]: `ssrLooseContain`, [SSR_RENDER_DYNAMIC_MODEL]: `ssrRenderDynamicModel`, [SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`, [SSR_RENDER_TELEPORT]: `ssrRenderTeleport`, [SSR_RENDER_SUSPENSE]: `ssrRenderSuspense`, [SSR_GET_DIRECTIVE_PROPS]: `ssrGetDirectiveProps` }; // Note: these are helpers imported from @vue/server-renderer // make sure the names match! compilerDom.registerRuntimeHelpers(ssrHelpers); // Plugin for the first transform pass, which simply constructs the AST node const ssrTransformIf = compilerDom.createStructuralDirectiveTransform(/^(if|else|else-if)$/, compilerDom.processIf); // This is called during the 2nd transform pass to construct the SSR-specific // codegen nodes. function ssrProcessIf(node, context, disableNestedFragments = false) { const [rootBranch] = node.branches; const ifStatement = compilerDom.createIfStatement(rootBranch.condition, processIfBranch(rootBranch, context, disableNestedFragments)); context.pushStatement(ifStatement); let currentIf = ifStatement; for (let i = 1; i < node.branches.length; i++) { const branch = node.branches[i]; const branchBlockStatement = processIfBranch(branch, context, disableNestedFragments); if (branch.condition) { // else-if currentIf = currentIf.alternate = compilerDom.createIfStatement(branch.condition, branchBlockStatement); } else { // else currentIf.alternate = branchBlockStatement; } } if (!currentIf.alternate) { currentIf.alternate = compilerDom.createBlockStatement([ compilerDom.createCallExpression(`_push`, ['``']) ]); } } function processIfBranch(branch, context, disableNestedFragments = false) { const { children } = branch; const needFragmentWrapper = !disableNestedFragments && (children.length !== 1 || children[0].type !== 1 /* ELEMENT */) && // optimize away nested fragments when the only child is a ForNode !(children.length === 1 && children[0].type === 11 /* FOR */); return processChildrenAsStatement(branch, context, needFragmentWrapper); } // Plugin for the first transform pass, which simply constructs the AST node const ssrTransformFor = compilerDom.createStructuralDirectiveTransform('for', compilerDom.processFor); // This is called during the 2nd transform pass to construct the SSR-specific // codegen nodes. function ssrProcessFor(node, context, disableNestedFragments = false) { const needFragmentWrapper = !disableNestedFragments && (node.children.length !== 1 || node.children[0].type !== 1 /* ELEMENT */); const renderLoop = compilerDom.createFunctionExpression(compilerDom.createForLoopParams(node.parseResult)); renderLoop.body = processChildrenAsStatement(node, context, needFragmentWrapper); // v-for always renders a fragment unless explicitly disabled if (!disableNestedFragments) { context.pushStringPart(``); } context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_LIST), [ node.source, renderLoop ])); if (!disableNestedFragments) { context.pushStringPart(``); } } const ssrTransformSlotOutlet = (node, context) => { if (compilerDom.isSlotOutlet(node)) { const { slotName, slotProps } = compilerDom.processSlotOutlet(node, context); const args = [ `_ctx.$slots`, slotName, slotProps || `{}`, // fallback content placeholder. will be replaced in the process phase `null`, `_push`, `_parent` ]; // inject slot scope id if current template uses :slotted if (context.scopeId && context.slotted !== false) { args.push(`"${context.scopeId}-s"`); } let method = SSR_RENDER_SLOT; // #3989 // check if this is a single slot inside a transition wrapper - since // transition will unwrap the slot fragment into a single vnode at runtime, // we need to avoid rendering the slot as a fragment. const parent = context.parent; if (parent && parent.type === 1 /* ELEMENT */ && parent.tagType === 1 /* COMPONENT */ && compilerDom.resolveComponentType(parent, context, true) === compilerDom.TRANSITION && parent.children.filter(c => c.type === 1 /* ELEMENT */).length === 1) { method = SSR_RENDER_SLOT_INNER; if (!(context.scopeId && context.slotted !== false)) { args.push('null'); } args.push('true'); } node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(method), args); } }; function ssrProcessSlotOutlet(node, context) { const renderCall = node.ssrCodegenNode; // has fallback content if (node.children.length) { const fallbackRenderFn = compilerDom.createFunctionExpression([]); fallbackRenderFn.body = processChildrenAsStatement(node, context); // _renderSlot(slots, name, props, fallback, ...) renderCall.arguments[3] = fallbackRenderFn; } // Forwarded . Merge slot scope ids if (context.withSlotScopeId) { const slotScopeId = renderCall.arguments[6]; renderCall.arguments[6] = slotScopeId ? `${slotScopeId} + _scopeId` : `_scopeId`; } context.pushStatement(node.ssrCodegenNode); } function createSSRCompilerError(code, loc) { return compilerDom.createCompilerError(code, loc, SSRErrorMessages); } const SSRErrorMessages = { [61 /* X_SSR_UNSAFE_ATTR_NAME */]: `Unsafe attribute name for SSR.`, [62 /* X_SSR_NO_TELEPORT_TARGET */]: `Missing the 'to' prop on teleport element.`, [63 /* X_SSR_INVALID_AST_NODE */]: `Invalid AST node during SSR transform.` }; // Note: this is a 2nd-pass codegen transform. function ssrProcessTeleport(node, context) { const targetProp = compilerDom.findProp(node, 'to'); if (!targetProp) { context.onError(createSSRCompilerError(62 /* X_SSR_NO_TELEPORT_TARGET */, node.loc)); return; } let target; if (targetProp.type === 6 /* ATTRIBUTE */) { target = targetProp.value && compilerDom.createSimpleExpression(targetProp.value.content, true); } else { target = targetProp.exp; } if (!target) { context.onError(createSSRCompilerError(62 /* X_SSR_NO_TELEPORT_TARGET */, targetProp.loc)); return; } const disabledProp = compilerDom.findProp(node, 'disabled', false, true /* allow empty */); const disabled = disabledProp ? disabledProp.type === 6 /* ATTRIBUTE */ ? `true` : disabledProp.exp || `false` : `false`; const contentRenderFn = compilerDom.createFunctionExpression([`_push`], undefined, // Body is added later true, // newline false, // isSlot node.loc); contentRenderFn.body = processChildrenAsStatement(node, context); context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_TELEPORT), [ `_push`, contentRenderFn, target, disabled, `_parent` ])); } const wipMap = new WeakMap(); // phase 1 function ssrTransformSuspense(node, context) { return () => { if (node.children.length) { const wipEntry = { slotsExp: null, wipSlots: [] }; wipMap.set(node, wipEntry); wipEntry.slotsExp = compilerDom.buildSlots(node, context, (_props, children, loc) => { const fn = compilerDom.createFunctionExpression([], undefined, // no return, assign body later true, // newline false, // suspense slots are not treated as normal slots loc); wipEntry.wipSlots.push({ fn, children }); return fn; }).slots; } }; } // phase 2 function ssrProcessSuspense(node, context) { // complete wip slots with ssr code const wipEntry = wipMap.get(node); if (!wipEntry) { return; } const { slotsExp, wipSlots } = wipEntry; for (let i = 0; i < wipSlots.length; i++) { const slot = wipSlots[i]; slot.fn.body = processChildrenAsStatement(slot, context); } // _push(ssrRenderSuspense(slots)) context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_SUSPENSE), [ `_push`, slotsExp ])); } // for directives with children overwrite (e.g. v-html & v-text), we need to // store the raw children so that they can be added in the 2nd pass. const rawChildrenMap = new WeakMap(); const ssrTransformElement = (node, context) => { if (node.type !== 1 /* ELEMENT */ || node.tagType !== 0 /* ELEMENT */) { return; } return function ssrPostTransformElement() { // element // generate the template literal representing the open tag. const openTag = [`<${node.tag}`]; // some tags need to be passed to runtime for special checks const needTagForRuntime = node.tag === 'textarea' || node.tag.indexOf('-') > 0; // v-bind="obj", v-bind:[key] and custom directives can potentially // overwrite other static attrs and can affect final rendering result, // so when they are present we need to bail out to full `renderAttrs` const hasDynamicVBind = compilerDom.hasDynamicKeyVBind(node); const hasCustomDir = node.props.some(p => p.type === 7 /* DIRECTIVE */ && !shared.isBuiltInDirective(p.name)); const needMergeProps = hasDynamicVBind || hasCustomDir; if (needMergeProps) { const { props, directives } = compilerDom.buildProps(node, context, node.props, false /* isComponent */, false /* isDynamicComponent */, true /* ssr */); if (props || directives.length) { const mergedProps = buildSSRProps(props, directives, context); const propsExp = compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTRS), [mergedProps]); if (node.tag === 'textarea') { const existingText = node.children[0]; // If interpolation, this is dynamic