compiler-ssr.cjs.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var compilerDom = require('@vue/compiler-dom');
  4. var shared = require('@vue/shared');
  5. const SSR_INTERPOLATE = Symbol(`ssrInterpolate`);
  6. const SSR_RENDER_VNODE = Symbol(`ssrRenderVNode`);
  7. const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`);
  8. const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`);
  9. const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`);
  10. const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`);
  11. const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`);
  12. const SSR_RENDER_ATTR = Symbol(`ssrRenderAttr`);
  13. const SSR_RENDER_DYNAMIC_ATTR = Symbol(`ssrRenderDynamicAttr`);
  14. const SSR_RENDER_LIST = Symbol(`ssrRenderList`);
  15. const SSR_INCLUDE_BOOLEAN_ATTR = Symbol(`ssrIncludeBooleanAttr`);
  16. const SSR_LOOSE_EQUAL = Symbol(`ssrLooseEqual`);
  17. const SSR_LOOSE_CONTAIN = Symbol(`ssrLooseContain`);
  18. const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`);
  19. const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`);
  20. const SSR_RENDER_TELEPORT = Symbol(`ssrRenderTeleport`);
  21. const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`);
  22. const SSR_GET_DIRECTIVE_PROPS = Symbol(`ssrGetDirectiveProps`);
  23. const ssrHelpers = {
  24. [SSR_INTERPOLATE]: `ssrInterpolate`,
  25. [SSR_RENDER_VNODE]: `ssrRenderVNode`,
  26. [SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
  27. [SSR_RENDER_SLOT]: `ssrRenderSlot`,
  28. [SSR_RENDER_CLASS]: `ssrRenderClass`,
  29. [SSR_RENDER_STYLE]: `ssrRenderStyle`,
  30. [SSR_RENDER_ATTRS]: `ssrRenderAttrs`,
  31. [SSR_RENDER_ATTR]: `ssrRenderAttr`,
  32. [SSR_RENDER_DYNAMIC_ATTR]: `ssrRenderDynamicAttr`,
  33. [SSR_RENDER_LIST]: `ssrRenderList`,
  34. [SSR_INCLUDE_BOOLEAN_ATTR]: `ssrIncludeBooleanAttr`,
  35. [SSR_LOOSE_EQUAL]: `ssrLooseEqual`,
  36. [SSR_LOOSE_CONTAIN]: `ssrLooseContain`,
  37. [SSR_RENDER_DYNAMIC_MODEL]: `ssrRenderDynamicModel`,
  38. [SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`,
  39. [SSR_RENDER_TELEPORT]: `ssrRenderTeleport`,
  40. [SSR_RENDER_SUSPENSE]: `ssrRenderSuspense`,
  41. [SSR_GET_DIRECTIVE_PROPS]: `ssrGetDirectiveProps`
  42. };
  43. // Note: these are helpers imported from @vue/server-renderer
  44. // make sure the names match!
  45. compilerDom.registerRuntimeHelpers(ssrHelpers);
  46. // Plugin for the first transform pass, which simply constructs the AST node
  47. const ssrTransformIf = compilerDom.createStructuralDirectiveTransform(/^(if|else|else-if)$/, compilerDom.processIf);
  48. // This is called during the 2nd transform pass to construct the SSR-specific
  49. // codegen nodes.
  50. function ssrProcessIf(node, context, disableNestedFragments = false) {
  51. const [rootBranch] = node.branches;
  52. const ifStatement = compilerDom.createIfStatement(rootBranch.condition, processIfBranch(rootBranch, context, disableNestedFragments));
  53. context.pushStatement(ifStatement);
  54. let currentIf = ifStatement;
  55. for (let i = 1; i < node.branches.length; i++) {
  56. const branch = node.branches[i];
  57. const branchBlockStatement = processIfBranch(branch, context, disableNestedFragments);
  58. if (branch.condition) {
  59. // else-if
  60. currentIf = currentIf.alternate = compilerDom.createIfStatement(branch.condition, branchBlockStatement);
  61. }
  62. else {
  63. // else
  64. currentIf.alternate = branchBlockStatement;
  65. }
  66. }
  67. if (!currentIf.alternate) {
  68. currentIf.alternate = compilerDom.createBlockStatement([
  69. compilerDom.createCallExpression(`_push`, ['`<!---->`'])
  70. ]);
  71. }
  72. }
  73. function processIfBranch(branch, context, disableNestedFragments = false) {
  74. const { children } = branch;
  75. const needFragmentWrapper = !disableNestedFragments &&
  76. (children.length !== 1 || children[0].type !== 1 /* ELEMENT */) &&
  77. // optimize away nested fragments when the only child is a ForNode
  78. !(children.length === 1 && children[0].type === 11 /* FOR */);
  79. return processChildrenAsStatement(children, context, needFragmentWrapper);
  80. }
  81. // Plugin for the first transform pass, which simply constructs the AST node
  82. const ssrTransformFor = compilerDom.createStructuralDirectiveTransform('for', compilerDom.processFor);
  83. // This is called during the 2nd transform pass to construct the SSR-specific
  84. // codegen nodes.
  85. function ssrProcessFor(node, context, disableNestedFragments = false) {
  86. const needFragmentWrapper = !disableNestedFragments &&
  87. (node.children.length !== 1 || node.children[0].type !== 1 /* ELEMENT */);
  88. const renderLoop = compilerDom.createFunctionExpression(compilerDom.createForLoopParams(node.parseResult));
  89. renderLoop.body = processChildrenAsStatement(node.children, context, needFragmentWrapper);
  90. // v-for always renders a fragment unless explicitly disabled
  91. if (!disableNestedFragments) {
  92. context.pushStringPart(`<!--[-->`);
  93. }
  94. context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_LIST), [
  95. node.source,
  96. renderLoop
  97. ]));
  98. if (!disableNestedFragments) {
  99. context.pushStringPart(`<!--]-->`);
  100. }
  101. }
  102. const ssrTransformSlotOutlet = (node, context) => {
  103. if (compilerDom.isSlotOutlet(node)) {
  104. const { slotName, slotProps } = compilerDom.processSlotOutlet(node, context);
  105. const args = [
  106. `_ctx.$slots`,
  107. slotName,
  108. slotProps || `{}`,
  109. // fallback content placeholder. will be replaced in the process phase
  110. `null`,
  111. `_push`,
  112. `_parent`
  113. ];
  114. // inject slot scope id if current template uses :slotted
  115. if (context.scopeId && context.slotted !== false) {
  116. args.push(`"${context.scopeId}-s"`);
  117. }
  118. node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_SLOT), args);
  119. }
  120. };
  121. function ssrProcessSlotOutlet(node, context) {
  122. const renderCall = node.ssrCodegenNode;
  123. // has fallback content
  124. if (node.children.length) {
  125. const fallbackRenderFn = compilerDom.createFunctionExpression([]);
  126. fallbackRenderFn.body = processChildrenAsStatement(node.children, context);
  127. // _renderSlot(slots, name, props, fallback, ...)
  128. renderCall.arguments[3] = fallbackRenderFn;
  129. }
  130. // Forwarded <slot/>. Merge slot scope ids
  131. if (context.withSlotScopeId) {
  132. const slotScopeId = renderCall.arguments[6];
  133. renderCall.arguments[6] = slotScopeId
  134. ? `${slotScopeId} + _scopeId`
  135. : `_scopeId`;
  136. }
  137. context.pushStatement(node.ssrCodegenNode);
  138. }
  139. function createSSRCompilerError(code, loc) {
  140. return compilerDom.createCompilerError(code, loc, SSRErrorMessages);
  141. }
  142. const SSRErrorMessages = {
  143. [61 /* X_SSR_UNSAFE_ATTR_NAME */]: `Unsafe attribute name for SSR.`,
  144. [62 /* X_SSR_NO_TELEPORT_TARGET */]: `Missing the 'to' prop on teleport element.`,
  145. [63 /* X_SSR_INVALID_AST_NODE */]: `Invalid AST node during SSR transform.`
  146. };
  147. // Note: this is a 2nd-pass codegen transform.
  148. function ssrProcessTeleport(node, context) {
  149. const targetProp = compilerDom.findProp(node, 'to');
  150. if (!targetProp) {
  151. context.onError(createSSRCompilerError(62 /* X_SSR_NO_TELEPORT_TARGET */, node.loc));
  152. return;
  153. }
  154. let target;
  155. if (targetProp.type === 6 /* ATTRIBUTE */) {
  156. target =
  157. targetProp.value && compilerDom.createSimpleExpression(targetProp.value.content, true);
  158. }
  159. else {
  160. target = targetProp.exp;
  161. }
  162. if (!target) {
  163. context.onError(createSSRCompilerError(62 /* X_SSR_NO_TELEPORT_TARGET */, targetProp.loc));
  164. return;
  165. }
  166. const disabledProp = compilerDom.findProp(node, 'disabled', false, true /* allow empty */);
  167. const disabled = disabledProp
  168. ? disabledProp.type === 6 /* ATTRIBUTE */
  169. ? `true`
  170. : disabledProp.exp || `false`
  171. : `false`;
  172. const contentRenderFn = compilerDom.createFunctionExpression([`_push`], undefined, // Body is added later
  173. true, // newline
  174. false, // isSlot
  175. node.loc);
  176. contentRenderFn.body = processChildrenAsStatement(node.children, context);
  177. context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_TELEPORT), [
  178. `_push`,
  179. contentRenderFn,
  180. target,
  181. disabled,
  182. `_parent`
  183. ]));
  184. }
  185. const wipMap = new WeakMap();
  186. // phase 1
  187. function ssrTransformSuspense(node, context) {
  188. return () => {
  189. if (node.children.length) {
  190. const wipEntry = {
  191. slotsExp: null,
  192. wipSlots: []
  193. };
  194. wipMap.set(node, wipEntry);
  195. wipEntry.slotsExp = compilerDom.buildSlots(node, context, (_props, children, loc) => {
  196. const fn = compilerDom.createFunctionExpression([], undefined, // no return, assign body later
  197. true, // newline
  198. false, // suspense slots are not treated as normal slots
  199. loc);
  200. wipEntry.wipSlots.push({
  201. fn,
  202. children
  203. });
  204. return fn;
  205. }).slots;
  206. }
  207. };
  208. }
  209. // phase 2
  210. function ssrProcessSuspense(node, context) {
  211. // complete wip slots with ssr code
  212. const wipEntry = wipMap.get(node);
  213. if (!wipEntry) {
  214. return;
  215. }
  216. const { slotsExp, wipSlots } = wipEntry;
  217. for (let i = 0; i < wipSlots.length; i++) {
  218. const { fn, children } = wipSlots[i];
  219. fn.body = processChildrenAsStatement(children, context);
  220. }
  221. // _push(ssrRenderSuspense(slots))
  222. context.pushStatement(compilerDom.createCallExpression(context.helper(SSR_RENDER_SUSPENSE), [
  223. `_push`,
  224. slotsExp
  225. ]));
  226. }
  227. function ssrProcessTransitionGroup(node, context) {
  228. const tag = compilerDom.findProp(node, 'tag');
  229. if (tag) {
  230. if (tag.type === 7 /* DIRECTIVE */) {
  231. // dynamic :tag
  232. context.pushStringPart(`<`);
  233. context.pushStringPart(tag.exp);
  234. context.pushStringPart(`>`);
  235. processChildren(node.children, context, false,
  236. /**
  237. * TransitionGroup has the special runtime behavior of flattening and
  238. * concatenating all children into a single fragment (in order for them to
  239. * be patched using the same key map) so we need to account for that here
  240. * by disabling nested fragment wrappers from being generated.
  241. */
  242. true);
  243. context.pushStringPart(`</`);
  244. context.pushStringPart(tag.exp);
  245. context.pushStringPart(`>`);
  246. }
  247. else {
  248. // static tag
  249. context.pushStringPart(`<${tag.value.content}>`);
  250. processChildren(node.children, context, false, true);
  251. context.pushStringPart(`</${tag.value.content}>`);
  252. }
  253. }
  254. else {
  255. // fragment
  256. processChildren(node.children, context, true, true);
  257. }
  258. }
  259. // for directives with children overwrite (e.g. v-html & v-text), we need to
  260. // store the raw children so that they can be added in the 2nd pass.
  261. const rawChildrenMap = new WeakMap();
  262. const ssrTransformElement = (node, context) => {
  263. if (node.type !== 1 /* ELEMENT */ ||
  264. node.tagType !== 0 /* ELEMENT */) {
  265. return;
  266. }
  267. return function ssrPostTransformElement() {
  268. // element
  269. // generate the template literal representing the open tag.
  270. const openTag = [`<${node.tag}`];
  271. // some tags need to be passed to runtime for special checks
  272. const needTagForRuntime = node.tag === 'textarea' || node.tag.indexOf('-') > 0;
  273. // v-bind="obj", v-bind:[key] and custom directives can potentially
  274. // overwrite other static attrs and can affect final rendering result,
  275. // so when they are present we need to bail out to full `renderAttrs`
  276. const hasDynamicVBind = compilerDom.hasDynamicKeyVBind(node);
  277. const hasCustomDir = node.props.some(p => p.type === 7 /* DIRECTIVE */ && !shared.isBuiltInDirective(p.name));
  278. const needMergeProps = hasDynamicVBind || hasCustomDir;
  279. if (needMergeProps) {
  280. const { props, directives } = compilerDom.buildProps(node, context, node.props, true /* ssr */);
  281. if (props || directives.length) {
  282. const mergedProps = buildSSRProps(props, directives, context);
  283. const propsExp = compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTRS), [mergedProps]);
  284. if (node.tag === 'textarea') {
  285. const existingText = node.children[0];
  286. // If interpolation, this is dynamic <textarea> content, potentially
  287. // injected by v-model and takes higher priority than v-bind value
  288. if (!existingText || existingText.type !== 5 /* INTERPOLATION */) {
  289. // <textarea> with dynamic v-bind. We don't know if the final props
  290. // will contain .value, so we will have to do something special:
  291. // assign the merged props to a temp variable, and check whether
  292. // it contains value (if yes, render is as children).
  293. const tempId = `_temp${context.temps++}`;
  294. propsExp.arguments = [
  295. compilerDom.createAssignmentExpression(compilerDom.createSimpleExpression(tempId, false), mergedProps)
  296. ];
  297. rawChildrenMap.set(node, compilerDom.createCallExpression(context.helper(SSR_INTERPOLATE), [
  298. compilerDom.createConditionalExpression(compilerDom.createSimpleExpression(`"value" in ${tempId}`, false), compilerDom.createSimpleExpression(`${tempId}.value`, false), compilerDom.createSimpleExpression(existingText ? existingText.content : ``, true), false)
  299. ]));
  300. }
  301. }
  302. else if (node.tag === 'input') {
  303. // <input v-bind="obj" v-model>
  304. // we need to determine the props to render for the dynamic v-model
  305. // and merge it with the v-bind expression.
  306. const vModel = findVModel(node);
  307. if (vModel) {
  308. // 1. save the props (san v-model) in a temp variable
  309. const tempId = `_temp${context.temps++}`;
  310. const tempExp = compilerDom.createSimpleExpression(tempId, false);
  311. propsExp.arguments = [
  312. compilerDom.createSequenceExpression([
  313. compilerDom.createAssignmentExpression(tempExp, mergedProps),
  314. compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), [
  315. tempExp,
  316. compilerDom.createCallExpression(context.helper(SSR_GET_DYNAMIC_MODEL_PROPS), [
  317. tempExp,
  318. vModel.exp // model
  319. ])
  320. ])
  321. ])
  322. ];
  323. }
  324. }
  325. if (needTagForRuntime) {
  326. propsExp.arguments.push(`"${node.tag}"`);
  327. }
  328. openTag.push(propsExp);
  329. }
  330. }
  331. // book keeping static/dynamic class merging.
  332. let dynamicClassBinding = undefined;
  333. let staticClassBinding = undefined;
  334. // all style bindings are converted to dynamic by transformStyle.
  335. // but we need to make sure to merge them.
  336. let dynamicStyleBinding = undefined;
  337. for (let i = 0; i < node.props.length; i++) {
  338. const prop = node.props[i];
  339. // ignore true-value/false-value on input
  340. if (node.tag === 'input' && isTrueFalseValue(prop)) {
  341. continue;
  342. }
  343. // special cases with children override
  344. if (prop.type === 7 /* DIRECTIVE */) {
  345. if (prop.name === 'html' && prop.exp) {
  346. rawChildrenMap.set(node, prop.exp);
  347. }
  348. else if (prop.name === 'text' && prop.exp) {
  349. node.children = [compilerDom.createInterpolation(prop.exp, prop.loc)];
  350. }
  351. else if (prop.name === 'slot') {
  352. context.onError(compilerDom.createCompilerError(40 /* X_V_SLOT_MISPLACED */, prop.loc));
  353. }
  354. else if (isTextareaWithValue(node, prop) && prop.exp) {
  355. if (!needMergeProps) {
  356. node.children = [compilerDom.createInterpolation(prop.exp, prop.loc)];
  357. }
  358. }
  359. else if (!needMergeProps) {
  360. // Directive transforms.
  361. const directiveTransform = context.directiveTransforms[prop.name];
  362. if (directiveTransform) {
  363. const { props, ssrTagParts } = directiveTransform(prop, node, context);
  364. if (ssrTagParts) {
  365. openTag.push(...ssrTagParts);
  366. }
  367. for (let j = 0; j < props.length; j++) {
  368. const { key, value } = props[j];
  369. if (compilerDom.isStaticExp(key)) {
  370. let attrName = key.content;
  371. // static key attr
  372. if (attrName === 'key' || attrName === 'ref') {
  373. continue;
  374. }
  375. if (attrName === 'class') {
  376. openTag.push(` class="`, (dynamicClassBinding = compilerDom.createCallExpression(context.helper(SSR_RENDER_CLASS), [value])), `"`);
  377. }
  378. else if (attrName === 'style') {
  379. if (dynamicStyleBinding) {
  380. // already has style binding, merge into it.
  381. mergeCall(dynamicStyleBinding, value);
  382. }
  383. else {
  384. openTag.push(` style="`, (dynamicStyleBinding = compilerDom.createCallExpression(context.helper(SSR_RENDER_STYLE), [value])), `"`);
  385. }
  386. }
  387. else {
  388. attrName =
  389. node.tag.indexOf('-') > 0
  390. ? attrName // preserve raw name on custom elements
  391. : shared.propsToAttrMap[attrName] || attrName.toLowerCase();
  392. if (shared.isBooleanAttr(attrName)) {
  393. openTag.push(compilerDom.createConditionalExpression(compilerDom.createCallExpression(context.helper(SSR_INCLUDE_BOOLEAN_ATTR), [value]), compilerDom.createSimpleExpression(' ' + attrName, true), compilerDom.createSimpleExpression('', true), false /* no newline */));
  394. }
  395. else if (shared.isSSRSafeAttrName(attrName)) {
  396. openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_ATTR), [
  397. key,
  398. value
  399. ]));
  400. }
  401. else {
  402. context.onError(createSSRCompilerError(61 /* X_SSR_UNSAFE_ATTR_NAME */, key.loc));
  403. }
  404. }
  405. }
  406. else {
  407. // dynamic key attr
  408. // this branch is only encountered for custom directive
  409. // transforms that returns properties with dynamic keys
  410. const args = [key, value];
  411. if (needTagForRuntime) {
  412. args.push(`"${node.tag}"`);
  413. }
  414. openTag.push(compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_ATTR), args));
  415. }
  416. }
  417. }
  418. }
  419. }
  420. else {
  421. // special case: value on <textarea>
  422. if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
  423. rawChildrenMap.set(node, shared.escapeHtml(prop.value.content));
  424. }
  425. else if (!needMergeProps) {
  426. if (prop.name === 'key' || prop.name === 'ref') {
  427. continue;
  428. }
  429. // static prop
  430. if (prop.name === 'class' && prop.value) {
  431. staticClassBinding = JSON.stringify(prop.value.content);
  432. }
  433. openTag.push(` ${prop.name}` +
  434. (prop.value ? `="${shared.escapeHtml(prop.value.content)}"` : ``));
  435. }
  436. }
  437. }
  438. // handle co-existence of dynamic + static class bindings
  439. if (dynamicClassBinding && staticClassBinding) {
  440. mergeCall(dynamicClassBinding, staticClassBinding);
  441. removeStaticBinding(openTag, 'class');
  442. }
  443. if (context.scopeId) {
  444. openTag.push(` ${context.scopeId}`);
  445. }
  446. node.ssrCodegenNode = compilerDom.createTemplateLiteral(openTag);
  447. };
  448. };
  449. function buildSSRProps(props, directives, context) {
  450. let mergePropsArgs = [];
  451. if (props) {
  452. if (props.type === 14 /* JS_CALL_EXPRESSION */) {
  453. // already a mergeProps call
  454. mergePropsArgs = props.arguments;
  455. }
  456. else {
  457. mergePropsArgs.push(props);
  458. }
  459. }
  460. if (directives.length) {
  461. for (const dir of directives) {
  462. mergePropsArgs.push(compilerDom.createCallExpression(context.helper(SSR_GET_DIRECTIVE_PROPS), [
  463. `_ctx`,
  464. ...compilerDom.buildDirectiveArgs(dir, context).elements
  465. ]));
  466. }
  467. }
  468. return mergePropsArgs.length > 1
  469. ? compilerDom.createCallExpression(context.helper(compilerDom.MERGE_PROPS), mergePropsArgs)
  470. : mergePropsArgs[0];
  471. }
  472. function isTrueFalseValue(prop) {
  473. if (prop.type === 7 /* DIRECTIVE */) {
  474. return (prop.name === 'bind' &&
  475. prop.arg &&
  476. compilerDom.isStaticExp(prop.arg) &&
  477. (prop.arg.content === 'true-value' || prop.arg.content === 'false-value'));
  478. }
  479. else {
  480. return prop.name === 'true-value' || prop.name === 'false-value';
  481. }
  482. }
  483. function isTextareaWithValue(node, prop) {
  484. return !!(node.tag === 'textarea' &&
  485. prop.name === 'bind' &&
  486. compilerDom.isStaticArgOf(prop.arg, 'value'));
  487. }
  488. function mergeCall(call, arg) {
  489. const existing = call.arguments[0];
  490. if (existing.type === 17 /* JS_ARRAY_EXPRESSION */) {
  491. existing.elements.push(arg);
  492. }
  493. else {
  494. call.arguments[0] = compilerDom.createArrayExpression([existing, arg]);
  495. }
  496. }
  497. function removeStaticBinding(tag, binding) {
  498. const regExp = new RegExp(`^ ${binding}=".+"$`);
  499. const i = tag.findIndex(e => typeof e === 'string' && regExp.test(e));
  500. if (i > -1) {
  501. tag.splice(i, 1);
  502. }
  503. }
  504. function findVModel(node) {
  505. return node.props.find(p => p.type === 7 /* DIRECTIVE */ && p.name === 'model' && p.exp);
  506. }
  507. function ssrProcessElement(node, context) {
  508. const isVoidTag = context.options.isVoidTag || shared.NO;
  509. const elementsToAdd = node.ssrCodegenNode.elements;
  510. for (let j = 0; j < elementsToAdd.length; j++) {
  511. context.pushStringPart(elementsToAdd[j]);
  512. }
  513. // Handle slot scopeId
  514. if (context.withSlotScopeId) {
  515. context.pushStringPart(compilerDom.createSimpleExpression(`_scopeId`, false));
  516. }
  517. // close open tag
  518. context.pushStringPart(`>`);
  519. const rawChildren = rawChildrenMap.get(node);
  520. if (rawChildren) {
  521. context.pushStringPart(rawChildren);
  522. }
  523. else if (node.children.length) {
  524. processChildren(node.children, context);
  525. }
  526. if (!isVoidTag(node.tag)) {
  527. // push closing tag
  528. context.pushStringPart(`</${node.tag}>`);
  529. }
  530. }
  531. // We need to construct the slot functions in the 1st pass to ensure proper
  532. // scope tracking, but the children of each slot cannot be processed until
  533. // the 2nd pass, so we store the WIP slot functions in a weakMap during the 1st
  534. // pass and complete them in the 2nd pass.
  535. const wipMap$1 = new WeakMap();
  536. const componentTypeMap = new WeakMap();
  537. // ssr component transform is done in two phases:
  538. // In phase 1. we use `buildSlot` to analyze the children of the component into
  539. // WIP slot functions (it must be done in phase 1 because `buildSlot` relies on
  540. // the core transform context).
  541. // In phase 2. we convert the WIP slots from phase 1 into ssr-specific codegen
  542. // nodes.
  543. const ssrTransformComponent = (node, context) => {
  544. if (node.type !== 1 /* ELEMENT */ ||
  545. node.tagType !== 1 /* COMPONENT */) {
  546. return;
  547. }
  548. const component = compilerDom.resolveComponentType(node, context, true /* ssr */);
  549. componentTypeMap.set(node, component);
  550. if (shared.isSymbol(component)) {
  551. if (component === compilerDom.SUSPENSE) {
  552. return ssrTransformSuspense(node, context);
  553. }
  554. return; // built-in component: fallthrough
  555. }
  556. // Build the fallback vnode-based branch for the component's slots.
  557. // We need to clone the node into a fresh copy and use the buildSlots' logic
  558. // to get access to the children of each slot. We then compile them with
  559. // a child transform pipeline using vnode-based transforms (instead of ssr-
  560. // based ones), and save the result branch (a ReturnStatement) in an array.
  561. // The branch is retrieved when processing slots again in ssr mode.
  562. const vnodeBranches = [];
  563. const clonedNode = clone(node);
  564. return function ssrPostTransformComponent() {
  565. // Using the cloned node, build the normal VNode-based branches (for
  566. // fallback in case the child is render-fn based). Store them in an array
  567. // for later use.
  568. if (clonedNode.children.length) {
  569. compilerDom.buildSlots(clonedNode, context, (props, children) => {
  570. vnodeBranches.push(createVNodeSlotBranch(props, children, context));
  571. return compilerDom.createFunctionExpression(undefined);
  572. });
  573. }
  574. let propsExp = `null`;
  575. if (node.props.length) {
  576. // note we are not passing ssr: true here because for components, v-on
  577. // handlers should still be passed
  578. const { props, directives } = compilerDom.buildProps(node, context);
  579. if (props || directives.length) {
  580. propsExp = buildSSRProps(props, directives, context);
  581. }
  582. }
  583. const wipEntries = [];
  584. wipMap$1.set(node, wipEntries);
  585. const buildSSRSlotFn = (props, children, loc) => {
  586. const fn = compilerDom.createFunctionExpression([props || `_`, `_push`, `_parent`, `_scopeId`], undefined, // no return, assign body later
  587. true, // newline
  588. true, // isSlot
  589. loc);
  590. wipEntries.push({
  591. fn,
  592. children,
  593. // also collect the corresponding vnode branch built earlier
  594. vnodeBranch: vnodeBranches[wipEntries.length]
  595. });
  596. return fn;
  597. };
  598. const slots = node.children.length
  599. ? compilerDom.buildSlots(node, context, buildSSRSlotFn).slots
  600. : `null`;
  601. if (typeof component !== 'string') {
  602. // dynamic component that resolved to a `resolveDynamicComponent` call
  603. // expression - since the resolved result may be a plain element (string)
  604. // or a VNode, handle it with `renderVNode`.
  605. node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_VNODE), [
  606. `_push`,
  607. compilerDom.createCallExpression(context.helper(compilerDom.CREATE_VNODE), [
  608. component,
  609. propsExp,
  610. slots
  611. ]),
  612. `_parent`
  613. ]);
  614. }
  615. else {
  616. node.ssrCodegenNode = compilerDom.createCallExpression(context.helper(SSR_RENDER_COMPONENT), [component, propsExp, slots, `_parent`]);
  617. }
  618. };
  619. };
  620. function ssrProcessComponent(node, context) {
  621. const component = componentTypeMap.get(node);
  622. if (!node.ssrCodegenNode) {
  623. // this is a built-in component that fell-through.
  624. if (component === compilerDom.TELEPORT) {
  625. return ssrProcessTeleport(node, context);
  626. }
  627. else if (component === compilerDom.SUSPENSE) {
  628. return ssrProcessSuspense(node, context);
  629. }
  630. else if (component === compilerDom.TRANSITION_GROUP) {
  631. return ssrProcessTransitionGroup(node, context);
  632. }
  633. else {
  634. // real fall-through: Transition / KeepAlive
  635. // just render its children.
  636. processChildren(node.children, context);
  637. }
  638. }
  639. else {
  640. // finish up slot function expressions from the 1st pass.
  641. const wipEntries = wipMap$1.get(node) || [];
  642. for (let i = 0; i < wipEntries.length; i++) {
  643. const { fn, children, vnodeBranch } = wipEntries[i];
  644. // For each slot, we generate two branches: one SSR-optimized branch and
  645. // one normal vnode-based branch. The branches are taken based on the
  646. // presence of the 2nd `_push` argument (which is only present if the slot
  647. // is called by `_ssrRenderSlot`.
  648. fn.body = compilerDom.createIfStatement(compilerDom.createSimpleExpression(`_push`, false), processChildrenAsStatement(children, context, false, true /* withSlotScopeId */), vnodeBranch);
  649. }
  650. // component is inside a slot, inherit slot scope Id
  651. if (context.withSlotScopeId) {
  652. node.ssrCodegenNode.arguments.push(`_scopeId`);
  653. }
  654. if (typeof component === 'string') {
  655. // static component
  656. context.pushStatement(compilerDom.createCallExpression(`_push`, [node.ssrCodegenNode]));
  657. }
  658. else {
  659. // dynamic component (`resolveDynamicComponent` call)
  660. // the codegen node is a `renderVNode` call
  661. context.pushStatement(node.ssrCodegenNode);
  662. }
  663. }
  664. }
  665. const rawOptionsMap = new WeakMap();
  666. const [baseNodeTransforms, baseDirectiveTransforms] = compilerDom.getBaseTransformPreset(true);
  667. const vnodeNodeTransforms = [...baseNodeTransforms, ...compilerDom.DOMNodeTransforms];
  668. const vnodeDirectiveTransforms = Object.assign(Object.assign({}, baseDirectiveTransforms), compilerDom.DOMDirectiveTransforms);
  669. function createVNodeSlotBranch(props, children, parentContext) {
  670. // apply a sub-transform using vnode-based transforms.
  671. const rawOptions = rawOptionsMap.get(parentContext.root);
  672. const subOptions = Object.assign(Object.assign({}, rawOptions), {
  673. // overwrite with vnode-based transforms
  674. nodeTransforms: [
  675. ...vnodeNodeTransforms,
  676. ...(rawOptions.nodeTransforms || [])
  677. ], directiveTransforms: Object.assign(Object.assign({}, vnodeDirectiveTransforms), (rawOptions.directiveTransforms || {})) });
  678. // wrap the children with a wrapper template for proper children treatment.
  679. const wrapperNode = {
  680. type: 1 /* ELEMENT */,
  681. ns: 0 /* HTML */,
  682. tag: 'template',
  683. tagType: 3 /* TEMPLATE */,
  684. isSelfClosing: false,
  685. // important: provide v-slot="props" on the wrapper for proper
  686. // scope analysis
  687. props: [
  688. {
  689. type: 7 /* DIRECTIVE */,
  690. name: 'slot',
  691. exp: props,
  692. arg: undefined,
  693. modifiers: [],
  694. loc: compilerDom.locStub
  695. }
  696. ],
  697. children,
  698. loc: compilerDom.locStub,
  699. codegenNode: undefined
  700. };
  701. subTransform(wrapperNode, subOptions, parentContext);
  702. return compilerDom.createReturnStatement(children);
  703. }
  704. function subTransform(node, options, parentContext) {
  705. const childRoot = compilerDom.createRoot([node]);
  706. const childContext = compilerDom.createTransformContext(childRoot, options);
  707. // this sub transform is for vnode fallback branch so it should be handled
  708. // like normal render functions
  709. childContext.ssr = false;
  710. // inherit parent scope analysis state
  711. childContext.scopes = Object.assign({}, parentContext.scopes);
  712. childContext.identifiers = Object.assign({}, parentContext.identifiers);
  713. childContext.imports = parentContext.imports;
  714. // traverse
  715. compilerDom.traverseNode(childRoot, childContext);
  716. ['helpers', 'components', 'directives'].forEach(key => {
  717. childContext[key].forEach((value, helperKey) => {
  718. if (key === 'helpers') {
  719. const parentCount = parentContext.helpers.get(helperKey);
  720. if (parentCount === undefined) {
  721. parentContext.helpers.set(helperKey, value);
  722. }
  723. else {
  724. parentContext.helpers.set(helperKey, value + parentCount);
  725. }
  726. }
  727. else {
  728. parentContext[key].add(value);
  729. }
  730. });
  731. });
  732. // imports/hoists are not merged because:
  733. // - imports are only used for asset urls and should be consistent between
  734. // node/client branches
  735. // - hoists are not enabled for the client branch here
  736. }
  737. function clone(v) {
  738. if (shared.isArray(v)) {
  739. return v.map(clone);
  740. }
  741. else if (shared.isObject(v)) {
  742. const res = {};
  743. for (const key in v) {
  744. res[key] = clone(v[key]);
  745. }
  746. return res;
  747. }
  748. else {
  749. return v;
  750. }
  751. }
  752. // Because SSR codegen output is completely different from client-side output
  753. // (e.g. multiple elements can be concatenated into a single template literal
  754. // instead of each getting a corresponding call), we need to apply an extra
  755. // transform pass to convert the template AST into a fresh JS AST before
  756. // passing it to codegen.
  757. function ssrCodegenTransform(ast, options) {
  758. const context = createSSRTransformContext(ast, options);
  759. // inject SFC <style> CSS variables
  760. // we do this instead of inlining the expression to ensure the vars are
  761. // only resolved once per render
  762. if (options.ssrCssVars) {
  763. const varsExp = compilerDom.processExpression(compilerDom.createSimpleExpression(options.ssrCssVars, false), compilerDom.createTransformContext(compilerDom.createRoot([]), options));
  764. context.body.push(compilerDom.createCompoundExpression([`const _cssVars = { style: `, varsExp, `}`]));
  765. }
  766. const isFragment = ast.children.length > 1 && ast.children.some(c => !compilerDom.isText(c));
  767. processChildren(ast.children, context, isFragment);
  768. ast.codegenNode = compilerDom.createBlockStatement(context.body);
  769. // Finalize helpers.
  770. // We need to separate helpers imported from 'vue' vs. '@vue/server-renderer'
  771. ast.ssrHelpers = Array.from(new Set([...ast.helpers.filter(h => h in ssrHelpers), ...context.helpers]));
  772. ast.helpers = ast.helpers.filter(h => !(h in ssrHelpers));
  773. }
  774. function createSSRTransformContext(root, options, helpers = new Set(), withSlotScopeId = false) {
  775. const body = [];
  776. let currentString = null;
  777. return {
  778. root,
  779. options,
  780. body,
  781. helpers,
  782. withSlotScopeId,
  783. onError: options.onError ||
  784. (e => {
  785. throw e;
  786. }),
  787. helper(name) {
  788. helpers.add(name);
  789. return name;
  790. },
  791. pushStringPart(part) {
  792. if (!currentString) {
  793. const currentCall = compilerDom.createCallExpression(`_push`);
  794. body.push(currentCall);
  795. currentString = compilerDom.createTemplateLiteral([]);
  796. currentCall.arguments.push(currentString);
  797. }
  798. const bufferedElements = currentString.elements;
  799. const lastItem = bufferedElements[bufferedElements.length - 1];
  800. if (shared.isString(part) && shared.isString(lastItem)) {
  801. bufferedElements[bufferedElements.length - 1] += part;
  802. }
  803. else {
  804. bufferedElements.push(part);
  805. }
  806. },
  807. pushStatement(statement) {
  808. // close current string
  809. currentString = null;
  810. body.push(statement);
  811. }
  812. };
  813. }
  814. function createChildContext(parent, withSlotScopeId = parent.withSlotScopeId) {
  815. // ensure child inherits parent helpers
  816. return createSSRTransformContext(parent.root, parent.options, parent.helpers, withSlotScopeId);
  817. }
  818. function processChildren(children, context, asFragment = false, disableNestedFragments = false) {
  819. if (asFragment) {
  820. context.pushStringPart(`<!--[-->`);
  821. }
  822. for (let i = 0; i < children.length; i++) {
  823. const child = children[i];
  824. switch (child.type) {
  825. case 1 /* ELEMENT */:
  826. switch (child.tagType) {
  827. case 0 /* ELEMENT */:
  828. ssrProcessElement(child, context);
  829. break;
  830. case 1 /* COMPONENT */:
  831. ssrProcessComponent(child, context);
  832. break;
  833. case 2 /* SLOT */:
  834. ssrProcessSlotOutlet(child, context);
  835. break;
  836. case 3 /* TEMPLATE */:
  837. // TODO
  838. break;
  839. default:
  840. context.onError(createSSRCompilerError(63 /* X_SSR_INVALID_AST_NODE */, child.loc));
  841. // make sure we exhaust all possible types
  842. const exhaustiveCheck = child;
  843. return exhaustiveCheck;
  844. }
  845. break;
  846. case 2 /* TEXT */:
  847. context.pushStringPart(shared.escapeHtml(child.content));
  848. break;
  849. case 3 /* COMMENT */:
  850. // no need to escape comment here because the AST can only
  851. // contain valid comments.
  852. context.pushStringPart(`<!--${child.content}-->`);
  853. break;
  854. case 5 /* INTERPOLATION */:
  855. context.pushStringPart(compilerDom.createCallExpression(context.helper(SSR_INTERPOLATE), [child.content]));
  856. break;
  857. case 9 /* IF */:
  858. ssrProcessIf(child, context, disableNestedFragments);
  859. break;
  860. case 11 /* FOR */:
  861. ssrProcessFor(child, context, disableNestedFragments);
  862. break;
  863. case 10 /* IF_BRANCH */:
  864. // no-op - handled by ssrProcessIf
  865. break;
  866. case 12 /* TEXT_CALL */:
  867. case 8 /* COMPOUND_EXPRESSION */:
  868. // no-op - these two types can never appear as template child node since
  869. // `transformText` is not used during SSR compile.
  870. break;
  871. default:
  872. context.onError(createSSRCompilerError(63 /* X_SSR_INVALID_AST_NODE */, child.loc));
  873. // make sure we exhaust all possible types
  874. const exhaustiveCheck = child;
  875. return exhaustiveCheck;
  876. }
  877. }
  878. if (asFragment) {
  879. context.pushStringPart(`<!--]-->`);
  880. }
  881. }
  882. function processChildrenAsStatement(children, parentContext, asFragment = false, withSlotScopeId = parentContext.withSlotScopeId) {
  883. const childContext = createChildContext(parentContext, withSlotScopeId);
  884. processChildren(children, childContext, asFragment);
  885. return compilerDom.createBlockStatement(childContext.body);
  886. }
  887. const ssrTransformModel = (dir, node, context) => {
  888. const model = dir.exp;
  889. function checkDuplicatedValue() {
  890. const value = compilerDom.findProp(node, 'value');
  891. if (value) {
  892. context.onError(compilerDom.createDOMCompilerError(57 /* X_V_MODEL_UNNECESSARY_VALUE */, value.loc));
  893. }
  894. }
  895. if (node.tagType === 0 /* ELEMENT */) {
  896. const res = { props: [] };
  897. const defaultProps = [
  898. // default value binding for text type inputs
  899. compilerDom.createObjectProperty(`value`, model)
  900. ];
  901. if (node.tag === 'input') {
  902. const type = compilerDom.findProp(node, 'type');
  903. if (type) {
  904. const value = findValueBinding(node);
  905. if (type.type === 7 /* DIRECTIVE */) {
  906. // dynamic type
  907. res.ssrTagParts = [
  908. compilerDom.createCallExpression(context.helper(SSR_RENDER_DYNAMIC_MODEL), [
  909. type.exp,
  910. model,
  911. value
  912. ])
  913. ];
  914. }
  915. else if (type.value) {
  916. // static type
  917. switch (type.value.content) {
  918. case 'radio':
  919. res.props = [
  920. compilerDom.createObjectProperty(`checked`, compilerDom.createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
  921. model,
  922. value
  923. ]))
  924. ];
  925. break;
  926. case 'checkbox':
  927. const trueValueBinding = compilerDom.findProp(node, 'true-value');
  928. if (trueValueBinding) {
  929. const trueValue = trueValueBinding.type === 6 /* ATTRIBUTE */
  930. ? JSON.stringify(trueValueBinding.value.content)
  931. : trueValueBinding.exp;
  932. res.props = [
  933. compilerDom.createObjectProperty(`checked`, compilerDom.createCallExpression(context.helper(SSR_LOOSE_EQUAL), [
  934. model,
  935. trueValue
  936. ]))
  937. ];
  938. }
  939. else {
  940. res.props = [
  941. compilerDom.createObjectProperty(`checked`, compilerDom.createConditionalExpression(compilerDom.createCallExpression(`Array.isArray`, [model]), compilerDom.createCallExpression(context.helper(SSR_LOOSE_CONTAIN), [
  942. model,
  943. value
  944. ]), model))
  945. ];
  946. }
  947. break;
  948. case 'file':
  949. context.onError(compilerDom.createDOMCompilerError(56 /* X_V_MODEL_ON_FILE_INPUT_ELEMENT */, dir.loc));
  950. break;
  951. default:
  952. checkDuplicatedValue();
  953. res.props = defaultProps;
  954. break;
  955. }
  956. }
  957. }
  958. else if (compilerDom.hasDynamicKeyVBind(node)) ;
  959. else {
  960. // text type
  961. checkDuplicatedValue();
  962. res.props = defaultProps;
  963. }
  964. }
  965. else if (node.tag === 'textarea') {
  966. checkDuplicatedValue();
  967. node.children = [compilerDom.createInterpolation(model, model.loc)];
  968. }
  969. else if (node.tag === 'select') ;
  970. else {
  971. context.onError(compilerDom.createDOMCompilerError(54 /* X_V_MODEL_ON_INVALID_ELEMENT */, dir.loc));
  972. }
  973. return res;
  974. }
  975. else {
  976. // component v-model
  977. return compilerDom.transformModel(dir, node, context);
  978. }
  979. };
  980. function findValueBinding(node) {
  981. const valueBinding = compilerDom.findProp(node, 'value');
  982. return valueBinding
  983. ? valueBinding.type === 7 /* DIRECTIVE */
  984. ? valueBinding.exp
  985. : compilerDom.createSimpleExpression(valueBinding.value.content, true)
  986. : compilerDom.createSimpleExpression(`null`, false);
  987. }
  988. const ssrTransformShow = (dir, node, context) => {
  989. if (!dir.exp) {
  990. context.onError(compilerDom.createDOMCompilerError(58 /* X_V_SHOW_NO_EXPRESSION */));
  991. }
  992. return {
  993. props: [
  994. compilerDom.createObjectProperty(`style`, compilerDom.createConditionalExpression(dir.exp, compilerDom.createSimpleExpression(`null`, false), compilerDom.createObjectExpression([
  995. compilerDom.createObjectProperty(`display`, compilerDom.createSimpleExpression(`none`, true))
  996. ]), false /* no newline */))
  997. ]
  998. };
  999. };
  1000. const hasSingleChild = (node) => node.children.filter(n => n.type !== 3 /* COMMENT */).length === 1;
  1001. const ssrInjectFallthroughAttrs = (node, context) => {
  1002. // _attrs is provided as a function argument.
  1003. // mark it as a known identifier so that it doesn't get prefixed by
  1004. // transformExpression.
  1005. if (node.type === 0 /* ROOT */) {
  1006. context.identifiers._attrs = 1;
  1007. }
  1008. if (node.type === 1 /* ELEMENT */ &&
  1009. node.tagType === 1 /* COMPONENT */ &&
  1010. (compilerDom.isBuiltInType(node.tag, 'Transition') ||
  1011. compilerDom.isBuiltInType(node.tag, 'KeepAlive'))) {
  1012. if (hasSingleChild(node)) {
  1013. injectFallthroughAttrs(node.children[0]);
  1014. }
  1015. return;
  1016. }
  1017. const parent = context.parent;
  1018. if (!parent || parent.type !== 0 /* ROOT */) {
  1019. return;
  1020. }
  1021. if (node.type === 10 /* IF_BRANCH */ && hasSingleChild(node)) {
  1022. injectFallthroughAttrs(node.children[0]);
  1023. }
  1024. else if (hasSingleChild(parent)) {
  1025. injectFallthroughAttrs(node);
  1026. }
  1027. };
  1028. function injectFallthroughAttrs(node) {
  1029. if (node.type === 1 /* ELEMENT */ &&
  1030. (node.tagType === 0 /* ELEMENT */ ||
  1031. node.tagType === 1 /* COMPONENT */) &&
  1032. !compilerDom.findDir(node, 'for')) {
  1033. node.props.push({
  1034. type: 7 /* DIRECTIVE */,
  1035. name: 'bind',
  1036. arg: undefined,
  1037. exp: compilerDom.createSimpleExpression(`_attrs`, false),
  1038. modifiers: [],
  1039. loc: compilerDom.locStub
  1040. });
  1041. }
  1042. }
  1043. const ssrInjectCssVars = (node, context) => {
  1044. if (!context.ssrCssVars) {
  1045. return;
  1046. }
  1047. // _cssVars is initialized once per render function
  1048. // the code is injected in ssrCodegenTransform when creating the
  1049. // ssr transform context
  1050. if (node.type === 0 /* ROOT */) {
  1051. context.identifiers._cssVars = 1;
  1052. }
  1053. const parent = context.parent;
  1054. if (!parent || parent.type !== 0 /* ROOT */) {
  1055. return;
  1056. }
  1057. if (node.type === 10 /* IF_BRANCH */) {
  1058. for (const child of node.children) {
  1059. injectCssVars(child);
  1060. }
  1061. }
  1062. else {
  1063. injectCssVars(node);
  1064. }
  1065. };
  1066. function injectCssVars(node) {
  1067. if (node.type === 1 /* ELEMENT */ &&
  1068. (node.tagType === 0 /* ELEMENT */ ||
  1069. node.tagType === 1 /* COMPONENT */) &&
  1070. !compilerDom.findDir(node, 'for')) {
  1071. if (compilerDom.isBuiltInType(node.tag, 'Suspense')) {
  1072. for (const child of node.children) {
  1073. if (child.type === 1 /* ELEMENT */ &&
  1074. child.tagType === 3 /* TEMPLATE */) {
  1075. // suspense slot
  1076. child.children.forEach(injectCssVars);
  1077. }
  1078. else {
  1079. injectCssVars(child);
  1080. }
  1081. }
  1082. }
  1083. else {
  1084. node.props.push({
  1085. type: 7 /* DIRECTIVE */,
  1086. name: 'bind',
  1087. arg: undefined,
  1088. exp: compilerDom.createSimpleExpression(`_cssVars`, false),
  1089. modifiers: [],
  1090. loc: compilerDom.locStub
  1091. });
  1092. }
  1093. }
  1094. }
  1095. function compile(template, options = {}) {
  1096. options = Object.assign(Object.assign(Object.assign({}, options), compilerDom.parserOptions), { ssr: true, inSSR: true, scopeId: options.mode === 'function' ? null : options.scopeId,
  1097. // always prefix since compiler-ssr doesn't have size concern
  1098. prefixIdentifiers: true,
  1099. // disable optimizations that are unnecessary for ssr
  1100. cacheHandlers: false, hoistStatic: false });
  1101. const ast = compilerDom.baseParse(template, options);
  1102. // Save raw options for AST. This is needed when performing sub-transforms
  1103. // on slot vnode branches.
  1104. rawOptionsMap.set(ast, options);
  1105. compilerDom.transform(ast, Object.assign(Object.assign({}, options), { hoistStatic: false, nodeTransforms: [
  1106. ssrTransformIf,
  1107. ssrTransformFor,
  1108. compilerDom.trackVForSlotScopes,
  1109. compilerDom.transformExpression,
  1110. ssrTransformSlotOutlet,
  1111. ssrInjectFallthroughAttrs,
  1112. ssrInjectCssVars,
  1113. ssrTransformElement,
  1114. ssrTransformComponent,
  1115. compilerDom.trackSlotScopes,
  1116. compilerDom.transformStyle,
  1117. ...(options.nodeTransforms || []) // user transforms
  1118. ], directiveTransforms: Object.assign({
  1119. // reusing core v-bind
  1120. bind: compilerDom.transformBind,
  1121. // model and show has dedicated SSR handling
  1122. model: ssrTransformModel, show: ssrTransformShow,
  1123. // the following are ignored during SSR
  1124. on: compilerDom.noopDirectiveTransform, cloak: compilerDom.noopDirectiveTransform, once: compilerDom.noopDirectiveTransform, memo: compilerDom.noopDirectiveTransform }, (options.directiveTransforms || {}) // user transforms
  1125. ) }));
  1126. // traverse the template AST and convert into SSR codegen AST
  1127. // by replacing ast.codegenNode.
  1128. ssrCodegenTransform(ast, options);
  1129. return compilerDom.generate(ast, options);
  1130. }
  1131. exports.compile = compile;