compiler-ssr.cjs.js 50 KB

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