'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_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 ssrHelpers = {
[SSR_INTERPOLATE]: `ssrInterpolate`,
[SSR_RENDER_VNODE]: `ssrRenderVNode`,
[SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
[SSR_RENDER_SLOT]: `ssrRenderSlot`,
[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`
};
// 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(children, 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.children, 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"`);
}
node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_SLOT), args);
}
};
function ssrProcessSlotOutlet(node, context) {
const renderCall = node.ssrCodegenNode;
// has fallback content
if (node.children.length) {
const fallbackRenderFn = compilerDom.createFunctionExpression([]);
fallbackRenderFn.body = processChildrenAsStatement(node.children, 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_CUSTOM_DIRECTIVE_NO_TRANSFORM */]: `Custom directive is missing corresponding SSR transform and will be ignored.`,
[62 /* X_SSR_UNSAFE_ATTR_NAME */]: `Unsafe attribute name for SSR.`,
[63 /* X_SSR_NO_TELEPORT_TARGET */]: `Missing the 'to' prop on teleport element.`,
[64 /* 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(63 /* 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(63 /* 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.children, 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 { fn, children } = wipSlots[i];
fn.body = processChildrenAsStatement(children, context);
}
// _push(ssrRenderSuspense(slots))
context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_SUSPENSE), [
`_push`,
slotsExp
]));
}
function ssrProcessTransitionGroup(node, context) {
const tag = compilerDom.findProp(node, 'tag');
if (tag) {
if (tag.type === 7 /* DIRECTIVE */) {
// dynamic :tag
context.pushStringPart(`<`);
context.pushStringPart(tag.exp);
context.pushStringPart(`>`);
processChildren(node.children, context, false,
/**
* TransitionGroup has the special runtime behavior of flattening and
* concatenating all children into a single fragment (in order for them to
* be pathced using the same key map) so we need to account for that here
* by disabling nested fragment wrappers from being generated.
*/
true);
context.pushStringPart(``);
context.pushStringPart(tag.exp);
context.pushStringPart(`>`);
}
else {
// static tag
context.pushStringPart(`<${tag.value.content}>`);
processChildren(node.children, context, false, true);
context.pushStringPart(`${tag.value.content}>`);
}
}
else {
// fragment
processChildren(node.children, context, true, true);
}
}
// 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
// the 2nd pass, so we store the WIP slot functions in a weakmap during the 1st
// pass and complete them in the 2nd pass.
const wipMap$1 = new WeakMap();
const componentTypeMap = new WeakMap();
// ssr component transform is done in two phases:
// In phase 1. we use `buildSlot` to analyze the children of the component into
// WIP slot functions (it must be done in phase 1 because `buildSlot` relies on
// the core transform context).
// In phase 2. we convert the WIP slots from phase 1 into ssr-specific codegen
// nodes.
const ssrTransformComponent = (node, context) => {
if (node.type !== 1 /* ELEMENT */ ||
node.tagType !== 1 /* COMPONENT */) {
return;
}
const component = compilerDom.resolveComponentType(node, context, true /* ssr */);
componentTypeMap.set(node, component);
if (shared.isSymbol(component)) {
if (component === compilerDom.SUSPENSE) {
return ssrTransformSuspense(node, context);
}
return; // built-in component: fallthrough
}
// Build the fallback vnode-based branch for the component's slots.
// We need to clone the node into a fresh copy and use the buildSlots' logic
// to get access to the children of each slot. We then compile them with
// a child transform pipeline using vnode-based transforms (instead of ssr-
// based ones), and save the result branch (a ReturnStatement) in an array.
// The branch is retrieved when processing slots again in ssr mode.
const vnodeBranches = [];
const clonedNode = clone(node);
return function ssrPostTransformComponent() {
// Using the cloned node, build the normal VNode-based branches (for
// fallback in case the child is render-fn based). Store them in an array
// for later use.
if (clonedNode.children.length) {
compilerDom.buildSlots(clonedNode, context, (props, children) => {
vnodeBranches.push(createVNodeSlotBranch(props, children, context));
return compilerDom.createFunctionExpression(undefined);
});
}
const props = node.props.length > 0
? // note we are not passing ssr: true here because for components, v-on
// handlers should still be passed
compilerDom.buildProps(node, context).props || `null`
: `null`;
const wipEntries = [];
wipMap$1.set(node, wipEntries);
const buildSSRSlotFn = (props, children, loc) => {
const fn = compilerDom.createFunctionExpression([props || `_`, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later
true, // newline
true, // isSlot
loc);
wipEntries.push({
fn,
children,
// also collect the corresponding vnode branch built earlier
vnodeBranch: vnodeBranches[wipEntries.length]
});
return fn;
};
const slots = node.children.length
? compilerDom.buildSlots(node, context, buildSSRSlotFn).slots
: `null`;
if (typeof component !== 'string') {
// dynamic component that resolved to a `resolveDynamicComponent` call
// expression - since the resolved result may be a plain element (string)
// or a VNode, handle it with `renderVNode`.
node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_VNODE), [
`_push`,
compilerDom.createCallExpression(context.helper(compilerDom.CREATE_VNODE), [
component,
props,
slots
]),
`_parent`
]);
}
else {
node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_COMPONENT), [component, props, slots, `_parent`]);
}
};
};
function ssrProcessComponent(node, context) {
const component = componentTypeMap.get(node);
if (!node.ssrCodegenNode) {
// this is a built-in component that fell-through.
if (component === compilerDom.TELEPORT) {
return ssrProcessTeleport(node, context);
}
else if (component === compilerDom.SUSPENSE) {
return ssrProcessSuspense(node, context);
}
else if (component === compilerDom.TRANSITION_GROUP) {
return ssrProcessTransitionGroup(node, context);
}
else {
// real fall-through: Transition / KeepAlive
// just render its children.
processChildren(node.children, context);
}
}
else {
// finish up slot function expressions from the 1st pass.
const wipEntries = wipMap$1.get(node) || [];
for (let i = 0; i < wipEntries.length; i++) {
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 = compilerDom.createIfStatement(compilerDom.createSimpleExpression(`_push`, false), processChildrenAsStatement(children, context, false, true /* withSlotScopeId */), vnodeBranch);
}
// component is inside a slot, inherit slot scope Id
if (context.withSlotScopeId) {
node.ssrCodegenNode.arguments.push(`_scopeId`);
}
if (typeof component === 'string') {
// static component
context.pushStatement(compilerDom.createCallExpression(`_push`, [node.ssrCodegenNode]));
}
else {
// dynamic component (`resolveDynamicComponent` call)
// the codegen node is a `renderVNode` call
context.pushStatement(node.ssrCodegenNode);
}
}
}
const rawOptionsMap = new WeakMap();
const [baseNodeTransforms, baseDirectiveTransforms] = compilerDom.getBaseTransformPreset(true);
const vnodeNodeTransforms = [...baseNodeTransforms, ...compilerDom.DOMNodeTransforms];
const vnodeDirectiveTransforms = Object.assign(Object.assign({}, baseDirectiveTransforms), compilerDom.DOMDirectiveTransforms);
function createVNodeSlotBranch(props, children, parentContext) {
// apply a sub-transform using vnode-based transforms.
const rawOptions = rawOptionsMap.get(parentContext.root);
const subOptions = Object.assign(Object.assign({}, rawOptions), {
// overwrite with vnode-based transforms
nodeTransforms: [
...vnodeNodeTransforms,
...(rawOptions.nodeTransforms || [])
], directiveTransforms: Object.assign(Object.assign({}, vnodeDirectiveTransforms), (rawOptions.directiveTransforms || {})) });
// wrap the children with a wrapper template for proper children treatment.
const wrapperNode = {
type: 1 /* ELEMENT */,
ns: 0 /* HTML */,
tag: 'template',
tagType: 3 /* TEMPLATE */,
isSelfClosing: false,
// important: provide v-slot="props" on the wrapper for proper
// scope analysis
props: [
{
type: 7 /* DIRECTIVE */,
name: 'slot',
exp: props,
arg: undefined,
modifiers: [],
loc: compilerDom.locStub
}
],
children,
loc: compilerDom.locStub,
codegenNode: undefined
};
subTransform(wrapperNode, subOptions, parentContext);
return compilerDom.createReturnStatement(children);
}
function subTransform(node, options, parentContext) {
const childRoot = compilerDom.createRoot([node]);
const childContext = compilerDom.createTransformContext(childRoot, options);
// this sub transform is for vnode fallback branch so it should be handled
// like normal render functions
childContext.ssr = false;
// inherit parent scope analysis state
childContext.scopes = Object.assign({}, parentContext.scopes);
childContext.identifiers = Object.assign({}, parentContext.identifiers);
childContext.imports = parentContext.imports;
// traverse
compilerDom.traverseNode(childRoot, childContext);
['helpers', 'components', 'directives'].forEach(key => {
childContext[key].forEach((value, helperKey) => {
if (key === 'helpers') {
const parentCount = parentContext.helpers.get(helperKey);
if (parentCount === undefined) {
parentContext.helpers.set(helperKey, value);
}
else {
parentContext.helpers.set(helperKey, value + parentCount);
}
}
else {
parentContext[key].add(value);
}
});
});
// imports/hoists are not merged because:
// - imports are only used for asset urls and should be consistent between
// node/client branches
// - hoists are not enabled for the client branch here
}
function clone(v) {
if (shared.isArray(v)) {
return v.map(clone);
}
else if (shared.isObject(v)) {
const res = {};
for (const key in v) {
res[key] = clone(v[key]);
}
return res;
}
else {
return v;
}
}
// 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" or v-bind:[key] 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);
if (hasDynamicVBind) {
const { props } = compilerDom.buildProps(node, context, node.props, true /* ssr */);
if (props) {
const propsExp = compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTRS), [props]);
if (node.tag === 'textarea') {
const existingText = node.children[0];
// If interpolation, this is dynamic