compiler-ssr.cjs.js 55 KB

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