vSlot.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.createVSlotCallExpression = exports.rewriteScopedSlotVForScope = exports.findCurrentPath = exports.findSlotName = exports.rewriteVSlot = exports.transformSlot = void 0;
  4. const types_1 = require("@babel/types");
  5. const shared_1 = require("@vue/shared");
  6. const compiler_core_1 = require("@vue/compiler-core");
  7. const uni_shared_1 = require("@dcloudio/uni-shared");
  8. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  9. const runtimeHelpers_1 = require("../runtimeHelpers");
  10. const ast_1 = require("../ast");
  11. const codegen_1 = require("../codegen");
  12. const transform_1 = require("../transform");
  13. const utils_1 = require("./utils");
  14. const vFor_1 = require("./vFor");
  15. const runtimeHelpers_2 = require("../runtimeHelpers");
  16. const transformSlot = (node, context) => {
  17. if (!(0, uni_cli_shared_1.isUserComponent)(node, context)) {
  18. return;
  19. }
  20. const { tag, children } = node;
  21. const slots = new Set();
  22. const onComponentSlot = (0, compiler_core_1.findDir)(node, 'slot', true);
  23. const implicitDefaultChildren = [];
  24. const isMiniProgramComponent = context.isMiniProgramComponent(tag);
  25. for (let i = 0; i < children.length; i++) {
  26. const slotElement = children[i];
  27. let slotDir;
  28. if (!(0, compiler_core_1.isTemplateNode)(slotElement) ||
  29. !(slotDir = (0, compiler_core_1.findDir)(slotElement, 'slot', true))) {
  30. // not a <template v-slot>, skip.
  31. if (slotElement.type !== 3 /* NodeTypes.COMMENT */) {
  32. implicitDefaultChildren.push(slotElement);
  33. }
  34. continue;
  35. }
  36. if (onComponentSlot) {
  37. // already has on-component slot - this is incorrect usage.
  38. context.onError((0, compiler_core_1.createCompilerError)(37 /* ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));
  39. break;
  40. }
  41. if (!slotDir.arg) {
  42. // v-slot => v-slot:default
  43. slotDir.arg = (0, compiler_core_1.createSimpleExpression)('default', true);
  44. }
  45. const slotName = transformTemplateSlotElement(slotDir, slotElement, node, context);
  46. // 小程序组件默认插槽,直接移除<template #default>节点
  47. if (isMiniProgramComponent) {
  48. if (slotName === 'default' && slotElement.children.length === 1) {
  49. children.splice(i, 1, slotElement.children[0]);
  50. }
  51. continue;
  52. }
  53. if (slotName) {
  54. slots.add(slotName);
  55. }
  56. }
  57. if (isMiniProgramComponent) {
  58. return;
  59. }
  60. if (implicitDefaultChildren.length) {
  61. slots.add(uni_shared_1.SLOT_DEFAULT_NAME);
  62. }
  63. if (onComponentSlot) {
  64. // <unicloud-db v-slot:default="{data, loading, error, options}"/>
  65. // => <unicloud-db collection=""><template v-slot:default="{data, loading, error, options}"/></unicloud-db>
  66. slots.add(uni_shared_1.SLOT_DEFAULT_NAME);
  67. const templateNode = createTemplateNode(onComponentSlot, implicitDefaultChildren);
  68. transformTemplateSlotElement(onComponentSlot, templateNode, node, context);
  69. node.children = [templateNode];
  70. }
  71. // 不支持 $slots, 则自动补充 props
  72. if (slots.size && !context.miniProgram.slot.$slots) {
  73. const slotsArr = [...slots];
  74. const hasDynamic = slotsArr.find((name) => !(0, shared_1.isString)(name));
  75. let value;
  76. if (hasDynamic) {
  77. const children = [];
  78. const len = slotsArr.length - 1;
  79. slotsArr.forEach((name, index) => {
  80. if ((0, shared_1.isString)(name)) {
  81. children.push(`'${(0, uni_shared_1.dynamicSlotName)(name)}'`);
  82. }
  83. else {
  84. children.push(name);
  85. }
  86. if (index < len) {
  87. children.push(',');
  88. }
  89. });
  90. value = (0, compiler_core_1.createCompoundExpression)([
  91. context.helperString(runtimeHelpers_2.DYNAMIC_SLOT) + '([',
  92. ...children,
  93. '])',
  94. ]);
  95. }
  96. else {
  97. value = `[${slotsArr
  98. .map((name) => `'${(0, uni_shared_1.dynamicSlotName)(name)}'`)
  99. .join(',')}]`;
  100. }
  101. node.props.unshift((0, uni_cli_shared_1.createBindDirectiveNode)(utils_1.ATTR_VUE_SLOTS, value));
  102. }
  103. };
  104. exports.transformSlot = transformSlot;
  105. function rewriteVSlot(dir, context) {
  106. dir.arg = (0, utils_1.rewriteExpression)((0, compiler_core_1.createCompoundExpression)([
  107. context.helperString(runtimeHelpers_2.DYNAMIC_SLOT) + '(',
  108. dir.arg,
  109. ')',
  110. ]), context);
  111. }
  112. exports.rewriteVSlot = rewriteVSlot;
  113. function transformTemplateSlotElement(slotDir, slotTemplate, slotComponent, context) {
  114. const slotName = findSlotName(slotDir);
  115. if (!slotName) {
  116. return;
  117. }
  118. const { exp } = slotDir;
  119. // non scoped slots
  120. if (!exp) {
  121. return slotName;
  122. }
  123. // empty
  124. if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ && !exp.content.trim()) {
  125. return slotName;
  126. }
  127. // 使用vFor来简单处理scoped slot作用域问题
  128. slotTemplate.children = [
  129. createVForTemplate(slotTemplate, { name: slotName, value: (0, codegen_1.genExpr)(exp), slotComponent }, context),
  130. ];
  131. if (context.miniProgram.slot.dynamicSlotNames) {
  132. // 已经在 vFor 中补充 slot,故需要移除 slotTemplate 中的
  133. const index = slotTemplate.props.indexOf(slotDir);
  134. if (index > -1) {
  135. slotTemplate.props.splice(index, 1);
  136. }
  137. }
  138. // v-slot="slotProps" => v-slot 避免 transformIdentifier 生成 slotProps 的变量声明
  139. slotDir.exp = undefined;
  140. return slotName;
  141. }
  142. function createTemplateNode(slotDir, children) {
  143. return {
  144. type: 1 /* NodeTypes.ELEMENT */,
  145. tag: 'template',
  146. tagType: 3 /* ElementTypes.TEMPLATE */,
  147. loc: compiler_core_1.locStub,
  148. isSelfClosing: false,
  149. codegenNode: undefined,
  150. ns: 0,
  151. props: [slotDir],
  152. children,
  153. };
  154. }
  155. function findSlotName(slotDir) {
  156. if (!slotDir.arg) {
  157. return uni_shared_1.SLOT_DEFAULT_NAME;
  158. }
  159. if ((0, compiler_core_1.isStaticExp)(slotDir.arg)) {
  160. return slotDir.arg.content;
  161. }
  162. return slotDir.arg;
  163. }
  164. exports.findSlotName = findSlotName;
  165. function findCurrentVForValueAlias(context) {
  166. let scope = context.currentScope;
  167. while (scope) {
  168. if ((0, transform_1.isVForScope)(scope)) {
  169. return scope.valueAlias;
  170. }
  171. scope = scope.parent;
  172. }
  173. return '';
  174. }
  175. function createVForTemplate(slotElement, { name, value, slotComponent, }, context) {
  176. const slotName = 's' + context.scopes.vFor;
  177. const keyProp = (0, uni_cli_shared_1.createBindDirectiveNode)('key', 'i' + context.scopes.vFor);
  178. const source = (0, shared_1.isString)(name) ? `'${name}'` : (0, codegen_1.genExpr)(name);
  179. const vForProp = {
  180. type: 7 /* NodeTypes.DIRECTIVE */,
  181. name: 'for',
  182. loc: compiler_core_1.locStub,
  183. modifiers: [],
  184. arg: undefined,
  185. exp: (0, compiler_core_1.createSimpleExpression)(`(${value}, ${slotName}) in ${utils_1.SCOPED_SLOT_IDENTIFIER}(${source}, ${findCurrentVForValueAlias(context) || `''`})`),
  186. };
  187. const props = [vForProp, keyProp];
  188. if (context.miniProgram.slot.dynamicSlotNames) {
  189. props.push((0, uni_cli_shared_1.createBindDirectiveNode)('slot', slotName));
  190. }
  191. return {
  192. loc: slotElement.loc,
  193. ns: 0,
  194. tag: 'template',
  195. type: 1 /* NodeTypes.ELEMENT */,
  196. tagType: 3 /* ElementTypes.TEMPLATE */,
  197. props,
  198. isSelfClosing: false,
  199. codegenNode: undefined,
  200. children: slotElement.children,
  201. slotComponent,
  202. };
  203. }
  204. const slotNameRE = /\('(.*)',/;
  205. /**
  206. * ('default','') => default
  207. * @param source
  208. * @returns
  209. */
  210. function findCurrentSlotName(source) {
  211. return (0, types_1.stringLiteral)((0, uni_shared_1.dynamicSlotName)(source.children[1].match(slotNameRE)[1]));
  212. }
  213. function createPathBinaryExpr(scope, computed = true) {
  214. return (0, types_1.binaryExpression)('+', (0, types_1.binaryExpression)('+', (0, types_1.stringLiteral)(parseVForPath(scope.sourceAlias) + (computed ? '[' : '.')), (0, types_1.identifier)(scope.indexAlias)), (0, types_1.stringLiteral)(computed ? '].' : '.'));
  215. }
  216. function findCurrentPath(id, scope) {
  217. let parent = scope.parent;
  218. let binaryExpr = null;
  219. while (parent) {
  220. if ((0, transform_1.isVForScope)(parent)) {
  221. // const computed = !isScopedSlotVFor(parent)
  222. if (!binaryExpr) {
  223. binaryExpr = createPathBinaryExpr(parent);
  224. }
  225. else {
  226. binaryExpr = (0, types_1.binaryExpression)('+', createPathBinaryExpr(parent), binaryExpr);
  227. }
  228. }
  229. parent = parent.parent;
  230. }
  231. return ((binaryExpr && (0, types_1.binaryExpression)('+', binaryExpr, (0, types_1.stringLiteral)(id))) ||
  232. (0, types_1.stringLiteral)(id));
  233. }
  234. exports.findCurrentPath = findCurrentPath;
  235. function findCurrentVueIdExpr(node, context) {
  236. if (!node) {
  237. return (0, types_1.stringLiteral)('');
  238. }
  239. const vueIdProp = (0, compiler_core_1.findProp)(node, utils_1.ATTR_VUE_ID);
  240. if (vueIdProp.type === 6 /* NodeTypes.ATTRIBUTE */) {
  241. return (0, types_1.stringLiteral)(vueIdProp.value.content);
  242. }
  243. return (0, ast_1.parseExpr)((0, codegen_1.genExpr)(vueIdProp.exp), context) || (0, types_1.stringLiteral)('');
  244. }
  245. /**
  246. * 目前无用
  247. * @param vForScope
  248. * @param parentScope
  249. * @param context
  250. */
  251. function rewriteScopedSlotVForScope(vForScope, parentScope, context) {
  252. // 生成一个新的sourceAlias,用于scopedSlots
  253. const { source, sourceExpr } = vForScope;
  254. vForScope.sourceAlias = (0, utils_1.rewriteExpressionWithoutProperty)(source, context, sourceExpr, parentScope).content;
  255. }
  256. exports.rewriteScopedSlotVForScope = rewriteScopedSlotVForScope;
  257. function parseVForPath(id) {
  258. return id.includes('.') ? id.split('.')[1] : id;
  259. }
  260. function createVSlotCallExpression(slotComponent, vForScope, context) {
  261. const { source, sourceAlias } = vForScope;
  262. const id = parseVForPath(sourceAlias);
  263. return (0, types_1.callExpression)((0, types_1.identifier)(context.helperString(runtimeHelpers_1.WITH_SCOPED_SLOT)), [
  264. (0, vFor_1.createVForArrowFunctionExpression)(vForScope),
  265. (0, types_1.objectExpression)([
  266. // 插槽名称,数据更新 path,vueId
  267. (0, types_1.objectProperty)((0, types_1.identifier)('name'), findCurrentSlotName(source)),
  268. // 暂不生成 path
  269. (0, types_1.objectProperty)((0, types_1.identifier)('path'), findCurrentPath(id, vForScope)),
  270. (0, types_1.objectProperty)((0, types_1.identifier)('vueId'), findCurrentVueIdExpr(slotComponent, context)),
  271. ]),
  272. ]);
  273. }
  274. exports.createVSlotCallExpression = createVSlotCallExpression;