compiler-ssr.cjs.js 47 KB

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