123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272 |
- '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 /* NodeTypes.ELEMENT */) &&
- // optimize away nested fragments when the only child is a ForNode
- !(children.length === 1 && children[0].type === 11 /* NodeTypes.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 /* NodeTypes.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 /* NodeTypes.ELEMENT */ &&
- parent.tagType === 1 /* ElementTypes.COMPONENT */ &&
- compilerDom.resolveComponentType(parent, context, true) === compilerDom.TRANSITION &&
- parent.children.filter(c => c.type === 1 /* NodeTypes.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 <slot/>. 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 = {
- [62 /* SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME */]: `Unsafe attribute name for SSR.`,
- [63 /* SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET */]: `Missing the 'to' prop on teleport element.`,
- [64 /* SSRErrorCodes.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 /* SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET */, node.loc));
- return;
- }
- let target;
- if (targetProp.type === 6 /* NodeTypes.ATTRIBUTE */) {
- target =
- targetProp.value && compilerDom.createSimpleExpression(targetProp.value.content, true);
- }
- else {
- target = targetProp.exp;
- }
- if (!target) {
- context.onError(createSSRCompilerError(63 /* SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET */, targetProp.loc));
- return;
- }
- const disabledProp = compilerDom.findProp(node, 'disabled', false, true /* allow empty */);
- const disabled = disabledProp
- ? disabledProp.type === 6 /* NodeTypes.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 /* NodeTypes.ELEMENT */ ||
- node.tagType !== 0 /* ElementTypes.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 /* NodeTypes.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 <textarea> content, potentially
- // injected by v-model and takes higher priority than v-bind value
- if (!existingText || existingText.type !== 5 /* NodeTypes.INTERPOLATION */) {
- // <textarea> with dynamic v-bind. We don't know if the final props
- // will contain .value, so we will have to do something special:
- // assign the merged props to a temp variable, and check whether
- // it contains value (if yes, render is as children).
- const tempId = `_temp${context.temps++}`;
- propsExp.arguments = [
- compilerDom.createAssignmentExpression(compilerDom.createSimpleExpression(tempId, false), mergedProps)
- ];
- rawChildrenMap.set(node, compilerDom.createCallExpression(context.helper(SSR_INTERPOLATE), [
- compilerDom.createConditionalExpression(compilerDom.createSimpleExpression(`"value" in ${tempId}`, false), compilerDom.createSimpleExpression(`${tempId}.value`, false), compilerDom.createSimpleExpression(existingText ? existingText.content : ``, true), false)
- ]));
- }
- }
- else if (node.tag === 'input') {
- // <input v-bind="obj" v-model>
- // we need to determine the props to render for the dynamic v-model
- // and merge it with the v-bind expression.
- const vModel = findVModel(node);
- if (vModel) {
- // 1. save the props (san v-model) in a temp variable
- const tempId = `_temp${context.temps++}`;
- const tempExp = compilerDom.createSimpleExpression(tempId, false);
- propsExp.arguments = [
- compilerDom.createSequenceExpression([
- compilerDom.createAssignmentExpression(tempExp, mergedProps),
- compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), [
- tempExp,
- compilerDom.createCallExpression(context.helper(SSR_GET_DYNAMIC_MODEL_PROPS), [
- tempExp,
- vModel.exp // model
- ])
- ])
- ])
- ];
- }
- }
- if (needTagForRuntime) {
- propsExp.arguments.push(`"${node.tag}"`);
- }
- openTag.push(propsExp);
- }
- }
- // book keeping static/dynamic class merging.
- let dynamicClassBinding = undefined;
- let staticClassBinding = undefined;
- // all style bindings are converted to dynamic by transformStyle.
- // but we need to make sure to merge them.
- let dynamicStyleBinding = undefined;
- for (let i = 0; i < node.props.length; i++) {
- const prop = node.props[i];
- // ignore true-value/false-value on input
- if (node.tag === 'input' && isTrueFalseValue(prop)) {
- continue;
- }
- // special cases with children override
- if (prop.type === 7 /* NodeTypes.DIRECTIVE */) {
- if (prop.name === 'html' && prop.exp) {
- rawChildrenMap.set(node, prop.exp);
- }
- else if (prop.name === 'text' && prop.exp) {
- node.children = [compilerDom.createInterpolation(prop.exp, prop.loc)];
- }
- else if (prop.name === 'slot') {
- context.onError(compilerDom.createCompilerError(40 /* ErrorCodes.X_V_SLOT_MISPLACED */, prop.loc));
- }
- else if (isTextareaWithValue(node, prop) && prop.exp) {
- if (!needMergeProps) {
- node.children = [compilerDom.createInterpolation(prop.exp, prop.loc)];
- }
- }
- else if (!needMergeProps && prop.name !== 'on') {
- // Directive transforms.
- const directiveTransform = context.directiveTransforms[prop.name];
- if (directiveTransform) {
- const { props, ssrTagParts } = directiveTransform(prop, node, context);
- if (ssrTagParts) {
- openTag.push(...ssrTagParts);
- }
- for (let j = 0; j < props.length; j++) {
- const { key, value } = props[j];
- if (compilerDom.isStaticExp(key)) {
- let attrName = key.content;
- // static key attr
- if (attrName === 'key' || attrName === 'ref') {
- continue;
- }
- if (attrName === 'class') {
- openTag.push(` class="`, (dynamicClassBinding = compilerDom.createCallExpression(context.helper(SSR_RENDER_CLASS), [value])), `"`);
- }
- else if (attrName === 'style') {
- if (dynamicStyleBinding) {
- // already has style binding, merge into it.
- mergeCall(dynamicStyleBinding, value);
- }
- else {
- openTag.push(` style="`, (dynamicStyleBinding = compilerDom.createCallExpression(context.helper(SSR_RENDER_STYLE), [value])), `"`);
- }
- }
- else {
- attrName =
- node.tag.indexOf('-') > 0
- ? attrName // preserve raw name on custom elements
- : shared.propsToAttrMap[attrName] || attrName.toLowerCase();
- if (shared.isBooleanAttr(attrName)) {
- openTag.push(compilerDom.createConditionalExpression(compilerDom.createCallExpression(context.helper(SSR_INCLUDE_BOOLEAN_ATTR), [value]), compilerDom.createSimpleExpression(' ' + attrName, true), compilerDom.createSimpleExpression('', true), false /* no newline */));
- }
- else if (shared.isSSRSafeAttrName(attrName)) {
- openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTR), [
- key,
- value
- ]));
- }
- else {
- context.onError(createSSRCompilerError(62 /* SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME */, key.loc));
- }
- }
- }
- else {
- // dynamic key attr
- // this branch is only encountered for custom directive
- // transforms that returns properties with dynamic keys
- const args = [key, value];
- if (needTagForRuntime) {
- args.push(`"${node.tag}"`);
- }
- openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_ATTR), args));
- }
- }
- }
- }
- }
- else {
- // special case: value on <textarea>
- if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
- rawChildrenMap.set(node, shared.escapeHtml(prop.value.content));
- }
- else if (!needMergeProps) {
- if (prop.name === 'key' || prop.name === 'ref') {
- continue;
- }
- // static prop
- if (prop.name === 'class' && prop.value) {
- staticClassBinding = JSON.stringify(prop.value.content);
- }
- openTag.push(` ${prop.name}` +
- (prop.value ? `="${shared.escapeHtml(prop.value.content)}"` : ``));
- }
- }
- }
- // handle co-existence of dynamic + static class bindings
- if (dynamicClassBinding && staticClassBinding) {
- mergeCall(dynamicClassBinding, staticClassBinding);
- removeStaticBinding(openTag, 'class');
- }
- if (context.scopeId) {
- openTag.push(` ${context.scopeId}`);
- }
- node.ssrCodegenNode = compilerDom.createTemplateLiteral(openTag);
- };
- };
- function buildSSRProps(props, directives, context) {
- let mergePropsArgs = [];
- if (props) {
- if (props.type === 14 /* NodeTypes.JS_CALL_EXPRESSION */) {
- // already a mergeProps call
- mergePropsArgs = props.arguments;
- }
- else {
- mergePropsArgs.push(props);
- }
- }
- if (directives.length) {
- for (const dir of directives) {
- mergePropsArgs.push(compilerDom.createCallExpression(context.helper(SSR_GET_DIRECTIVE_PROPS), [
- `_ctx`,
- ...compilerDom.buildDirectiveArgs(dir, context).elements
- ]));
- }
- }
- return mergePropsArgs.length > 1
- ? compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), mergePropsArgs)
- : mergePropsArgs[0];
- }
- function isTrueFalseValue(prop) {
- if (prop.type === 7 /* NodeTypes.DIRECTIVE */) {
- return (prop.name === 'bind' &&
- prop.arg &&
- compilerDom.isStaticExp(prop.arg) &&
- (prop.arg.content === 'true-value' || prop.arg.content === 'false-value'));
- }
- else {
- return prop.name === 'true-value' || prop.name === 'false-value';
- }
- }
- function isTextareaWithValue(node, prop) {
- return !!(node.tag === 'textarea' &&
- prop.name === 'bind' &&
- compilerDom.isStaticArgOf(prop.arg, 'value'));
- }
- function mergeCall(call, arg) {
- const existing = call.arguments[0];
- if (existing.type === 17 /* NodeTypes.JS_ARRAY_EXPRESSION */) {
- existing.elements.push(arg);
- }
- else {
- call.arguments[0] = compilerDom.createArrayExpression([existing, arg]);
- }
- }
- function removeStaticBinding(tag, binding) {
- const regExp = new RegExp(`^ ${binding}=".+"$`);
- const i = tag.findIndex(e => typeof e === 'string' && regExp.test(e));
- if (i > -1) {
- tag.splice(i, 1);
- }
- }
- function findVModel(node) {
- return node.props.find(p => p.type === 7 /* NodeTypes.DIRECTIVE */ && p.name === 'model' && p.exp);
- }
- function ssrProcessElement(node, context) {
- const isVoidTag = context.options.isVoidTag || shared.NO;
- const elementsToAdd = node.ssrCodegenNode.elements;
- for (let j = 0; j < elementsToAdd.length; j++) {
- context.pushStringPart(elementsToAdd[j]);
- }
- // Handle slot scopeId
- if (context.withSlotScopeId) {
- context.pushStringPart(compilerDom.createSimpleExpression(`_scopeId`, false));
- }
- // close open tag
- context.pushStringPart(`>`);
- const rawChildren = rawChildrenMap.get(node);
- if (rawChildren) {
- context.pushStringPart(rawChildren);
- }
- else if (node.children.length) {
- processChildren(node, context);
- }
- if (!isVoidTag(node.tag)) {
- // push closing tag
- context.pushStringPart(`</${node.tag}>`);
- }
- }
- const wipMap$1 = new WeakMap();
- // phase 1: build props
- function ssrTransformTransitionGroup(node, context) {
- return () => {
- const tag = compilerDom.findProp(node, 'tag');
- if (tag) {
- const otherProps = node.props.filter(p => p !== tag);
- const { props, directives } = compilerDom.buildProps(node, context, otherProps, true /* isComponent */, false /* isDynamicComponent */, true /* ssr (skip event listeners) */);
- let propsExp = null;
- if (props || directives.length) {
- propsExp = compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTRS), [
- buildSSRProps(props, directives, context)
- ]);
- }
- wipMap$1.set(node, {
- tag,
- propsExp
- });
- }
- };
- }
- // phase 2: process children
- function ssrProcessTransitionGroup(node, context) {
- const entry = wipMap$1.get(node);
- if (entry) {
- const { tag, propsExp } = entry;
- if (tag.type === 7 /* NodeTypes.DIRECTIVE */) {
- // dynamic :tag
- context.pushStringPart(`<`);
- context.pushStringPart(tag.exp);
- if (propsExp) {
- context.pushStringPart(propsExp);
- }
- context.pushStringPart(`>`);
- processChildren(node, context, false,
- /**
- * TransitionGroup has the special runtime behavior of flattening and
- * concatenating all children into a single fragment (in order for them to
- * be patched 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}`);
- if (propsExp) {
- context.pushStringPart(propsExp);
- }
- context.pushStringPart(`>`);
- processChildren(node, context, false, true);
- context.pushStringPart(`</${tag.value.content}>`);
- }
- }
- else {
- // fragment
- processChildren(node, 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$2 = new WeakMap();
- const WIP_SLOT = Symbol();
- 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 /* NodeTypes.ELEMENT */ ||
- node.tagType !== 1 /* ElementTypes.COMPONENT */) {
- return;
- }
- const component = compilerDom.resolveComponentType(node, context, true /* ssr */);
- const isDynamicComponent = shared.isObject(component) && component.callee === compilerDom.RESOLVE_DYNAMIC_COMPONENT;
- componentTypeMap.set(node, component);
- if (shared.isSymbol(component)) {
- if (component === compilerDom.SUSPENSE) {
- return ssrTransformSuspense(node, context);
- }
- if (component === compilerDom.TRANSITION_GROUP) {
- return ssrTransformTransitionGroup(node, context);
- }
- return; // other built-in components: 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);
- });
- }
- let propsExp = `null`;
- if (node.props.length) {
- // note we are not passing ssr: true here because for components, v-on
- // handlers should still be passed
- const { props, directives } = compilerDom.buildProps(node, context, undefined, true, isDynamicComponent);
- if (props || directives.length) {
- propsExp = buildSSRProps(props, directives, context);
- }
- }
- const wipEntries = [];
- wipMap$2.set(node, wipEntries);
- const buildSSRSlotFn = (props, children, loc) => {
- const param0 = (props && compilerDom.stringifyExpression(props)) || `_`;
- const fn = compilerDom.createFunctionExpression([param0, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later
- true, // newline
- true, // isSlot
- loc);
- wipEntries.push({
- type: WIP_SLOT,
- 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,
- propsExp,
- slots
- ]),
- `_parent`
- ]);
- }
- else {
- node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_COMPONENT), [component, propsExp, slots, `_parent`]);
- }
- };
- };
- function ssrProcessComponent(node, context, parent) {
- 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.
- // #5352: if is at root level of a slot, push an empty string.
- // this does not affect the final output, but avoids all-comment slot
- // content of being treated as empty by ssrRenderSlot().
- if (parent.type === WIP_SLOT) {
- context.pushStringPart(``);
- }
- // #5351: filter out comment children inside transition
- if (component === compilerDom.TRANSITION) {
- node.children = node.children.filter(c => c.type !== 3 /* NodeTypes.COMMENT */);
- }
- processChildren(node, context);
- }
- }
- else {
- // finish up slot function expressions from the 1st pass.
- const wipEntries = wipMap$2.get(node) || [];
- for (let i = 0; i < wipEntries.length; i++) {
- const { fn, 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(wipEntries[i], 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 = {
- ...baseDirectiveTransforms,
- ...compilerDom.DOMDirectiveTransforms
- };
- function createVNodeSlotBranch(props, children, parentContext) {
- // apply a sub-transform using vnode-based transforms.
- const rawOptions = rawOptionsMap.get(parentContext.root);
- const subOptions = {
- ...rawOptions,
- // overwrite with vnode-based transforms
- nodeTransforms: [
- ...vnodeNodeTransforms,
- ...(rawOptions.nodeTransforms || [])
- ],
- directiveTransforms: {
- ...vnodeDirectiveTransforms,
- ...(rawOptions.directiveTransforms || {})
- }
- };
- // wrap the children with a wrapper template for proper children treatment.
- const wrapperNode = {
- type: 1 /* NodeTypes.ELEMENT */,
- ns: 0 /* Namespaces.HTML */,
- tag: 'template',
- tagType: 3 /* ElementTypes.TEMPLATE */,
- isSelfClosing: false,
- // important: provide v-slot="props" on the wrapper for proper
- // scope analysis
- props: [
- {
- type: 7 /* NodeTypes.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 = { ...parentContext.scopes };
- childContext.identifiers = { ...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;
- }
- }
- // Because SSR codegen output is completely different from client-side output
- // (e.g. multiple elements can be concatenated into a single template literal
- // instead of each getting a corresponding call), we need to apply an extra
- // transform pass to convert the template AST into a fresh JS AST before
- // passing it to codegen.
- function ssrCodegenTransform(ast, options) {
- const context = createSSRTransformContext(ast, options);
- // inject SFC <style> CSS variables
- // we do this instead of inlining the expression to ensure the vars are
- // only resolved once per render
- if (options.ssrCssVars) {
- const cssContext = compilerDom.createTransformContext(compilerDom.createRoot([]), options);
- const varsExp = compilerDom.processExpression(compilerDom.createSimpleExpression(options.ssrCssVars, false), cssContext);
- context.body.push(compilerDom.createCompoundExpression([`const _cssVars = { style: `, varsExp, `}`]));
- Array.from(cssContext.helpers.keys()).forEach(helper => {
- if (!ast.helpers.includes(helper))
- ast.helpers.push(helper);
- });
- }
- const isFragment = ast.children.length > 1 && ast.children.some(c => !compilerDom.isText(c));
- processChildren(ast, context, isFragment);
- ast.codegenNode = compilerDom.createBlockStatement(context.body);
- // Finalize helpers.
- // We need to separate helpers imported from 'vue' vs. '@vue/server-renderer'
- ast.ssrHelpers = Array.from(new Set([...ast.helpers.filter(h => h in ssrHelpers), ...context.helpers]));
- ast.helpers = ast.helpers.filter(h => !(h in ssrHelpers));
- }
- function createSSRTransformContext(root, options, helpers = new Set(), withSlotScopeId = false) {
- const body = [];
- let currentString = null;
- return {
- root,
- options,
- body,
- helpers,
- withSlotScopeId,
- onError: options.onError ||
- (e => {
- throw e;
- }),
- helper(name) {
- helpers.add(name);
- return name;
- },
- pushStringPart(part) {
- if (!currentString) {
- const currentCall = compilerDom.createCallExpression(`_push`);
- body.push(currentCall);
- currentString = compilerDom.createTemplateLiteral([]);
- currentCall.arguments.push(currentString);
- }
- const bufferedElements = currentString.elements;
- const lastItem = bufferedElements[bufferedElements.length - 1];
- if (shared.isString(part) && shared.isString(lastItem)) {
- bufferedElements[bufferedElements.length - 1] += part;
- }
- else {
- bufferedElements.push(part);
- }
- },
- pushStatement(statement) {
- // close current string
- currentString = null;
- body.push(statement);
- }
- };
- }
- function createChildContext(parent, withSlotScopeId = parent.withSlotScopeId) {
- // ensure child inherits parent helpers
- return createSSRTransformContext(parent.root, parent.options, parent.helpers, withSlotScopeId);
- }
- function processChildren(parent, context, asFragment = false, disableNestedFragments = false) {
- if (asFragment) {
- context.pushStringPart(`<!--[-->`);
- }
- const { children } = parent;
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- switch (child.type) {
- case 1 /* NodeTypes.ELEMENT */:
- switch (child.tagType) {
- case 0 /* ElementTypes.ELEMENT */:
- ssrProcessElement(child, context);
- break;
- case 1 /* ElementTypes.COMPONENT */:
- ssrProcessComponent(child, context, parent);
- break;
- case 2 /* ElementTypes.SLOT */:
- ssrProcessSlotOutlet(child, context);
- break;
- case 3 /* ElementTypes.TEMPLATE */:
- // TODO
- break;
- default:
- context.onError(createSSRCompilerError(64 /* SSRErrorCodes.X_SSR_INVALID_AST_NODE */, child.loc));
- // make sure we exhaust all possible types
- const exhaustiveCheck = child;
- return exhaustiveCheck;
- }
- break;
- case 2 /* NodeTypes.TEXT */:
- context.pushStringPart(shared.escapeHtml(child.content));
- break;
- case 3 /* NodeTypes.COMMENT */:
- // no need to escape comment here because the AST can only
- // contain valid comments.
- context.pushStringPart(`<!--${child.content}-->`);
- break;
- case 5 /* NodeTypes.INTERPOLATION */:
- context.pushStringPart(compilerDom.createCallExpression(context.helper(SSR_INTERPOLATE), [child.content]));
- break;
- case 9 /* NodeTypes.IF */:
- ssrProcessIf(child, context, disableNestedFragments);
- break;
- case 11 /* NodeTypes.FOR */:
- ssrProcessFor(child, context, disableNestedFragments);
- break;
- case 10 /* NodeTypes.IF_BRANCH */:
- // no-op - handled by ssrProcessIf
- break;
- case 12 /* NodeTypes.TEXT_CALL */:
- case 8 /* NodeTypes.COMPOUND_EXPRESSION */:
- // no-op - these two types can never appear as template child node since
- // `transformText` is not used during SSR compile.
- break;
- default:
- context.onError(createSSRCompilerError(64 /* SSRErrorCodes.X_SSR_INVALID_AST_NODE */, child.loc));
- // make sure we exhaust all possible types
- const exhaustiveCheck = child;
- return exhaustiveCheck;
- }
- }
- if (asFragment) {
- context.pushStringPart(`<!--]-->`);
- }
- }
- function processChildrenAsStatement(parent, parentContext, asFragment = false, withSlotScopeId = parentContext.withSlotScopeId) {
- const childContext = createChildContext(parentContext, withSlotScopeId);
- processChildren(parent, childContext, asFragment);
- return compilerDom.createBlockStatement(childContext.body);
- }
- const ssrTransformModel = (dir, node, context) => {
- const model = dir.exp;
- function checkDuplicatedValue() {
- const value = compilerDom.findProp(node, 'value');
- if (value) {
- context.onError(compilerDom.createDOMCompilerError(58 /* DOMErrorCodes.X_V_MODEL_UNNECESSARY_VALUE */, value.loc));
- }
- }
- if (node.tagType === 0 /* ElementTypes.ELEMENT */) {
- const res = { props: [] };
- const defaultProps = [
- // default value binding for text type inputs
- compilerDom.createObjectProperty(`value`, model)
- ];
- if (node.tag === 'input') {
- const type = compilerDom.findProp(node, 'type');
- if (type) {
- const value = findValueBinding(node);
- if (type.type === 7 /* NodeTypes.DIRECTIVE */) {
- // dynamic type
- res.ssrTagParts = [
- compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_MODEL), [
- type.exp,
- model,
- value
- ])
- ];
- }
- else if (type.value) {
- // static type
- switch (type.value.content) {
- case 'radio':
- res.props = [
- compilerDom.createObjectProperty(`checked`, compilerDom.createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
- model,
- value
- ]))
- ];
- break;
- case 'checkbox':
- const trueValueBinding = compilerDom.findProp(node, 'true-value');
- if (trueValueBinding) {
- const trueValue = trueValueBinding.type === 6 /* NodeTypes.ATTRIBUTE */
- ? JSON.stringify(trueValueBinding.value.content)
- : trueValueBinding.exp;
- res.props = [
- compilerDom.createObjectProperty(`checked`, compilerDom.createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
- model,
- trueValue
- ]))
- ];
- }
- else {
- res.props = [
- compilerDom.createObjectProperty(`checked`, compilerDom.createConditionalExpression(compilerDom.createCallExpression(`Array.isArray`, [model]), compilerDom.createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [
- model,
- value
- ]), model))
- ];
- }
- break;
- case 'file':
- context.onError(compilerDom.createDOMCompilerError(57 /* DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT */, dir.loc));
- break;
- default:
- checkDuplicatedValue();
- res.props = defaultProps;
- break;
- }
- }
- }
- else if (compilerDom.hasDynamicKeyVBind(node)) ;
- else {
- // text type
- checkDuplicatedValue();
- res.props = defaultProps;
- }
- }
- else if (node.tag === 'textarea') {
- checkDuplicatedValue();
- node.children = [compilerDom.createInterpolation(model, model.loc)];
- }
- else if (node.tag === 'select') ;
- else {
- context.onError(compilerDom.createDOMCompilerError(55 /* DOMErrorCodes.X_V_MODEL_ON_INVALID_ELEMENT */, dir.loc));
- }
- return res;
- }
- else {
- // component v-model
- return compilerDom.transformModel(dir, node, context);
- }
- };
- function findValueBinding(node) {
- const valueBinding = compilerDom.findProp(node, 'value');
- return valueBinding
- ? valueBinding.type === 7 /* NodeTypes.DIRECTIVE */
- ? valueBinding.exp
- : compilerDom.createSimpleExpression(valueBinding.value.content, true)
- : compilerDom.createSimpleExpression(`null`, false);
- }
- const ssrTransformShow = (dir, node, context) => {
- if (!dir.exp) {
- context.onError(compilerDom.createDOMCompilerError(59 /* DOMErrorCodes.X_V_SHOW_NO_EXPRESSION */));
- }
- return {
- props: [
- compilerDom.createObjectProperty(`style`, compilerDom.createConditionalExpression(dir.exp, compilerDom.createSimpleExpression(`null`, false), compilerDom.createObjectExpression([
- compilerDom.createObjectProperty(`display`, compilerDom.createSimpleExpression(`none`, true))
- ]), false /* no newline */))
- ]
- };
- };
- const filterChild = (node) => node.children.filter(n => n.type !== 3 /* NodeTypes.COMMENT */);
- const hasSingleChild = (node) => filterChild(node).length === 1;
- const ssrInjectFallthroughAttrs = (node, context) => {
- // _attrs is provided as a function argument.
- // mark it as a known identifier so that it doesn't get prefixed by
- // transformExpression.
- if (node.type === 0 /* NodeTypes.ROOT */) {
- context.identifiers._attrs = 1;
- }
- if (node.type === 1 /* NodeTypes.ELEMENT */ &&
- node.tagType === 1 /* ElementTypes.COMPONENT */ &&
- (compilerDom.isBuiltInType(node.tag, 'Transition') ||
- compilerDom.isBuiltInType(node.tag, 'KeepAlive'))) {
- const rootChildren = filterChild(context.root);
- if (rootChildren.length === 1 && rootChildren[0] === node) {
- if (hasSingleChild(node)) {
- injectFallthroughAttrs(node.children[0]);
- }
- return;
- }
- }
- const parent = context.parent;
- if (!parent || parent.type !== 0 /* NodeTypes.ROOT */) {
- return;
- }
- if (node.type === 10 /* NodeTypes.IF_BRANCH */ && hasSingleChild(node)) {
- // detect cases where the parent v-if is not the only root level node
- let hasEncounteredIf = false;
- for (const c of filterChild(parent)) {
- if (c.type === 9 /* NodeTypes.IF */ ||
- (c.type === 1 /* NodeTypes.ELEMENT */ && compilerDom.findDir(c, 'if'))) {
- // multiple root v-if
- if (hasEncounteredIf)
- return;
- hasEncounteredIf = true;
- }
- else if (
- // node before v-if
- !hasEncounteredIf ||
- // non else nodes
- !(c.type === 1 /* NodeTypes.ELEMENT */ && compilerDom.findDir(c, /else/, true))) {
- return;
- }
- }
- injectFallthroughAttrs(node.children[0]);
- }
- else if (hasSingleChild(parent)) {
- injectFallthroughAttrs(node);
- }
- };
- function injectFallthroughAttrs(node) {
- if (node.type === 1 /* NodeTypes.ELEMENT */ &&
- (node.tagType === 0 /* ElementTypes.ELEMENT */ ||
- node.tagType === 1 /* ElementTypes.COMPONENT */) &&
- !compilerDom.findDir(node, 'for')) {
- node.props.push({
- type: 7 /* NodeTypes.DIRECTIVE */,
- name: 'bind',
- arg: undefined,
- exp: compilerDom.createSimpleExpression(`_attrs`, false),
- modifiers: [],
- loc: compilerDom.locStub
- });
- }
- }
- const ssrInjectCssVars = (node, context) => {
- if (!context.ssrCssVars) {
- return;
- }
- // _cssVars is initialized once per render function
- // the code is injected in ssrCodegenTransform when creating the
- // ssr transform context
- if (node.type === 0 /* NodeTypes.ROOT */) {
- context.identifiers._cssVars = 1;
- }
- const parent = context.parent;
- if (!parent || parent.type !== 0 /* NodeTypes.ROOT */) {
- return;
- }
- if (node.type === 10 /* NodeTypes.IF_BRANCH */) {
- for (const child of node.children) {
- injectCssVars(child);
- }
- }
- else {
- injectCssVars(node);
- }
- };
- function injectCssVars(node) {
- if (node.type === 1 /* NodeTypes.ELEMENT */ &&
- (node.tagType === 0 /* ElementTypes.ELEMENT */ ||
- node.tagType === 1 /* ElementTypes.COMPONENT */) &&
- !compilerDom.findDir(node, 'for')) {
- if (compilerDom.isBuiltInType(node.tag, 'Suspense')) {
- for (const child of node.children) {
- if (child.type === 1 /* NodeTypes.ELEMENT */ &&
- child.tagType === 3 /* ElementTypes.TEMPLATE */) {
- // suspense slot
- child.children.forEach(injectCssVars);
- }
- else {
- injectCssVars(child);
- }
- }
- }
- else {
- node.props.push({
- type: 7 /* NodeTypes.DIRECTIVE */,
- name: 'bind',
- arg: undefined,
- exp: compilerDom.createSimpleExpression(`_cssVars`, false),
- modifiers: [],
- loc: compilerDom.locStub
- });
- }
- }
- }
- function compile(template, options = {}) {
- options = {
- ...options,
- // apply DOM-specific parsing options
- ...compilerDom.parserOptions,
- ssr: true,
- inSSR: true,
- scopeId: options.mode === 'function' ? null : options.scopeId,
- // always prefix since compiler-ssr doesn't have size concern
- prefixIdentifiers: true,
- // disable optimizations that are unnecessary for ssr
- cacheHandlers: false,
- hoistStatic: false
- };
- const ast = compilerDom.baseParse(template, options);
- // Save raw options for AST. This is needed when performing sub-transforms
- // on slot vnode branches.
- rawOptionsMap.set(ast, options);
- compilerDom.transform(ast, {
- ...options,
- hoistStatic: false,
- nodeTransforms: [
- ssrTransformIf,
- ssrTransformFor,
- compilerDom.trackVForSlotScopes,
- compilerDom.transformExpression,
- ssrTransformSlotOutlet,
- ssrInjectFallthroughAttrs,
- ssrInjectCssVars,
- ssrTransformElement,
- ssrTransformComponent,
- compilerDom.trackSlotScopes,
- compilerDom.transformStyle,
- ...(options.nodeTransforms || []) // user transforms
- ],
- directiveTransforms: {
- // reusing core v-bind
- bind: compilerDom.transformBind,
- on: compilerDom.transformOn,
- // model and show has dedicated SSR handling
- model: ssrTransformModel,
- show: ssrTransformShow,
- // the following are ignored during SSR
- // on: noopDirectiveTransform,
- cloak: compilerDom.noopDirectiveTransform,
- once: compilerDom.noopDirectiveTransform,
- memo: compilerDom.noopDirectiveTransform,
- ...(options.directiveTransforms || {}) // user transforms
- }
- });
- // traverse the template AST and convert into SSR codegen AST
- // by replacing ast.codegenNode.
- ssrCodegenTransform(ast, options);
- return compilerDom.generate(ast, options);
- }
- exports.compile = compile;
|