codegen.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.genElementProps = exports.genNode = exports.generate = void 0;
  4. const shared_1 = require("@vue/shared");
  5. const uni_shared_1 = require("@dcloudio/uni-shared");
  6. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  7. const compiler_core_1 = require("@vue/compiler-core");
  8. const codegen_1 = require("../codegen");
  9. const vFor_1 = require("../transforms/vFor");
  10. const vIf_1 = require("../transforms/vIf");
  11. const vSlot_1 = require("../transforms/vSlot");
  12. const utils_1 = require("../transforms/utils");
  13. function generate({ children }, { slot, event, scopeId, emitFile, filename, directive, lazyElement, isBuiltInComponent, isMiniProgramComponent, component, }) {
  14. const context = {
  15. slot,
  16. event,
  17. code: '',
  18. scopeId,
  19. directive,
  20. lazyElement,
  21. component,
  22. isBuiltInComponent,
  23. isMiniProgramComponent,
  24. push(code) {
  25. context.code += code;
  26. },
  27. };
  28. children.forEach((node) => {
  29. genNode(node, context);
  30. });
  31. emitFile({ type: 'asset', fileName: filename, source: context.code });
  32. }
  33. exports.generate = generate;
  34. function genNode(node, context) {
  35. switch (node.type) {
  36. case 9 /* NodeTypes.IF */:
  37. return node.branches.forEach((node) => {
  38. genNode(node, context);
  39. });
  40. case 2 /* NodeTypes.TEXT */:
  41. return genText(node, context);
  42. case 5 /* NodeTypes.INTERPOLATION */:
  43. return genExpression(node.content, context);
  44. case 1 /* NodeTypes.ELEMENT */:
  45. if (node.tagType === 2 /* ElementTypes.SLOT */) {
  46. return genSlot(node, context);
  47. }
  48. else if (node.tagType === 1 /* ElementTypes.COMPONENT */) {
  49. return genComponent(node, context);
  50. }
  51. else if (node.tagType === 3 /* ElementTypes.TEMPLATE */) {
  52. return genTemplate(node, context);
  53. }
  54. else if (isLazyElement(node, context)) {
  55. return genLazyElement(node, context);
  56. }
  57. return genElement(node, context);
  58. }
  59. }
  60. exports.genNode = genNode;
  61. function genText(node, { push }) {
  62. push(node.content);
  63. }
  64. function genExpression(node, { push }) {
  65. push(`{{${(0, codegen_1.genExpr)(node)}}}`);
  66. }
  67. function genVIf(exp, { push, directive }) {
  68. push(` ${directive}if="{{${exp}}}"`);
  69. }
  70. function genVElseIf(exp, { push, directive }) {
  71. push(` ${directive}elif="{{${exp}}}"`);
  72. }
  73. function genVElse({ push, directive }) {
  74. push(` ${directive}else`);
  75. }
  76. function genVFor(node, { push, directive }) {
  77. const { sourceCode, valueAlias, indexAlias } = node.vFor;
  78. push(` ${directive}for="${sourceCode}"`);
  79. if (valueAlias) {
  80. push(` ${directive}for-item="${valueAlias}"`);
  81. }
  82. if (valueAlias === 'index') {
  83. push(` ${directive}for-index="${indexAlias}"`);
  84. }
  85. const keyProp = (0, compiler_core_1.findProp)(node, 'key', true);
  86. if (keyProp) {
  87. const key = keyProp.exp.content;
  88. push(` ${directive}key="${key.includes('.') ? key.split('.')[1] : key}"`);
  89. node.props.splice(node.props.indexOf(keyProp), 1);
  90. }
  91. }
  92. function genSlot(node, context) {
  93. // 移除掉所有非name属性,即移除作用域插槽的绑定指令
  94. node.props = node.props.filter((prop) => {
  95. if (prop.type === 6 /* NodeTypes.ATTRIBUTE */) {
  96. return prop.name === 'name';
  97. }
  98. else if (prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
  99. return prop.arg.content === 'name';
  100. }
  101. });
  102. if (!node.children.length || context.slot.fallbackContent) {
  103. // 无后备内容或支持后备内容
  104. return genElement(node, context);
  105. }
  106. const { push } = context;
  107. const isVIfSlot = (0, vIf_1.isIfElementNode)(node);
  108. if (isVIfSlot) {
  109. push(`<block`);
  110. genVIfCode(node, context);
  111. push(`>`);
  112. delete node.vIf;
  113. }
  114. const children = node.children.slice();
  115. node.children.length = 0;
  116. push(`<block`);
  117. const nameProp = (0, compiler_core_1.findProp)(node, 'name');
  118. let name = uni_shared_1.SLOT_DEFAULT_NAME;
  119. if (nameProp) {
  120. if ((0, uni_cli_shared_1.isAttributeNode)(nameProp)) {
  121. if (nameProp.value?.content) {
  122. name = nameProp.value.content;
  123. }
  124. }
  125. else {
  126. if (nameProp.slotName) {
  127. name = nameProp.slotName;
  128. }
  129. }
  130. }
  131. genVIf(`$slots.${name}`, context);
  132. push(`>`);
  133. genElement(node, context);
  134. push(`</block>`);
  135. push(`<block`);
  136. genVElse(context);
  137. push(`>`);
  138. children.forEach((node) => {
  139. genNode(node, context);
  140. });
  141. push(`</block>`);
  142. if (isVIfSlot) {
  143. push(`</block>`);
  144. }
  145. }
  146. function genTemplate(node, context) {
  147. const slotProp = node.props.find((prop) => prop.type === 7 /* NodeTypes.DIRECTIVE */ &&
  148. (prop.name === 'slot' ||
  149. (prop.name === 'bind' &&
  150. prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
  151. prop.arg.content === 'slot')));
  152. // 为 bind 时,通常是作用域插槽生成的 vSlot.ts:197 createBindDirectiveNode('slot',...)
  153. if (slotProp && (slotProp.name === 'bind' || (0, vSlot_1.findSlotName)(slotProp))) {
  154. /**
  155. * 仅百度、字节支持使用 block 作为命名插槽根节点
  156. * 此处为了统一仅默认替换为view
  157. * <template v-slot/> => <view slot="">
  158. */
  159. node.tag = 'view';
  160. }
  161. else {
  162. // <template/> => <block/>
  163. node.tag = 'block';
  164. }
  165. // @ts-ignore
  166. node.tagType = 0 /* ElementTypes.ELEMENT */;
  167. // 仅单个子节点的命名插槽(非作用域),直接使用子节点作为插槽使用,避免多增加的 view 节点影响 flex 排版
  168. if (slotProp &&
  169. node.tag === 'view' &&
  170. !(0, vFor_1.isForElementNode)(node) &&
  171. node.children.length === 1) {
  172. const child = node.children[0];
  173. if ((0, uni_cli_shared_1.isElementNode)(child) &&
  174. !(0, vFor_1.isForElementNode)(child) &&
  175. !(0, compiler_core_1.isSlotOutlet)(child)) {
  176. child.props.push(slotProp);
  177. if ((0, vIf_1.isIfElementNode)(node)) {
  178. ;
  179. child.vIf = node.vIf;
  180. }
  181. return genElement(child, context);
  182. }
  183. }
  184. return genElement(node, context);
  185. }
  186. function genComponent(node, context) {
  187. if (context.component?.getPropertySync) {
  188. return genElement(node, context);
  189. }
  190. if ((0, vIf_1.isIfElementNode)(node) || (0, vFor_1.isForElementNode)(node)) {
  191. return genElement(node, context);
  192. }
  193. // 小程序原生组件,补充 if(r0)
  194. if (context.isMiniProgramComponent(node.tag)) {
  195. ;
  196. node.vIf = {
  197. name: 'if',
  198. condition: 'r0',
  199. };
  200. return genElement(node, context);
  201. }
  202. const prop = (0, compiler_core_1.findProp)(node, utils_1.ATTR_VUE_PROPS);
  203. if (!prop) {
  204. return genElement(node, context);
  205. }
  206. ;
  207. node.vIf = {
  208. name: 'if',
  209. condition: prop.exp.content,
  210. };
  211. return genElement(node, context);
  212. }
  213. function isLazyElement(node, context) {
  214. if (!context.lazyElement) {
  215. return false;
  216. }
  217. let lazyProps;
  218. if ((0, shared_1.isFunction)(context.lazyElement)) {
  219. const res = context.lazyElement(node, context);
  220. if (!(0, shared_1.isPlainObject)(res)) {
  221. return res;
  222. }
  223. lazyProps = res[node.tag];
  224. }
  225. else {
  226. lazyProps = context.lazyElement[node.tag];
  227. }
  228. if (lazyProps === true) {
  229. return true;
  230. }
  231. if (!lazyProps) {
  232. return;
  233. }
  234. return node.props.some((prop) => prop.type === 7 /* NodeTypes.DIRECTIVE */ &&
  235. lazyProps.find((lazyProp) => {
  236. return (prop.name === lazyProp.name &&
  237. prop.arg?.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
  238. lazyProp.arg.includes(prop.arg.content));
  239. }));
  240. }
  241. /**
  242. * 部分内置组件的部分事件在初始化时会立刻触发,但标准事件需要等首次渲染才能确认事件函数,故增加wx:if="{{r0}}"
  243. * @param node
  244. * @param context
  245. */
  246. function genLazyElement(node, context) {
  247. const { push } = context;
  248. if (!(0, vIf_1.isIfElementNode)(node)) {
  249. push(`<block`);
  250. // r0 => ready 首次渲染
  251. genVIf(`r0`, context);
  252. push(`>`);
  253. genElement(node, context);
  254. push(`</block>`);
  255. return;
  256. }
  257. // v-if,v-else-if 无需处理
  258. if (node.vIf.name !== 'else') {
  259. return genElement(node, context);
  260. }
  261. push(`<block`);
  262. genVElse(context);
  263. push(`>`);
  264. node.vIf.name = 'if';
  265. node.vIf.condition = 'r0';
  266. genElement(node, context);
  267. push(`</block>`);
  268. }
  269. function genVIfCode(node, context) {
  270. const { name, condition } = node.vIf;
  271. if (name === 'if') {
  272. genVIf(condition, context);
  273. }
  274. else if (name === 'else-if') {
  275. genVElseIf(condition, context);
  276. }
  277. else if (name === 'else') {
  278. genVElse(context);
  279. }
  280. }
  281. function genElement(node, context) {
  282. const { children, isSelfClosing, props } = node;
  283. let tag = node.tag;
  284. // <template slot="left"/> => <block slot="left"/>
  285. if (tag === 'template') {
  286. if ((0, compiler_core_1.findProp)(node, 'slot')) {
  287. tag = 'view';
  288. }
  289. else {
  290. tag = 'block';
  291. }
  292. }
  293. // 无用的 block
  294. if (tag === 'block' &&
  295. props.length === 0 &&
  296. !(0, vIf_1.isIfElementNode)(node) &&
  297. !(0, vFor_1.isForElementNode)(node)) {
  298. return children.forEach((node) => {
  299. genNode(node, context);
  300. });
  301. }
  302. let virtualHost = false;
  303. if ((0, uni_cli_shared_1.isUserComponent)(node, context)) {
  304. tag = (0, shared_1.hyphenate)(tag);
  305. if (context.component?.normalizeName) {
  306. tag = context.component?.normalizeName(tag);
  307. }
  308. if (context.component?.mergeVirtualHostAttributes) {
  309. virtualHost = true;
  310. }
  311. }
  312. const { push } = context;
  313. const hasVIf = (0, vIf_1.isIfElementNode)(node);
  314. const hasVFor = (0, vFor_1.isForElementNode)(node);
  315. const hasVIfAndVFor = hasVIf && hasVFor;
  316. // 小程序中 wx:else wx:elif 不支持与 wx:for 同时使用
  317. // 故 if 需要补充一层 block
  318. if (hasVIfAndVFor) {
  319. push(`<block`);
  320. genVIfCode(node, context);
  321. push(`>`);
  322. }
  323. push(`<${tag}`);
  324. if (!hasVIfAndVFor && hasVIf) {
  325. genVIfCode(node, context);
  326. }
  327. if (hasVFor) {
  328. genVFor(node, context);
  329. }
  330. if (props.length) {
  331. genElementProps(node, virtualHost, context);
  332. }
  333. if (isSelfClosing) {
  334. push(`/>`);
  335. }
  336. else {
  337. push(`>`);
  338. children.forEach((node) => {
  339. genNode(node, context);
  340. });
  341. push(`</${tag}>`);
  342. }
  343. if (hasVIfAndVFor) {
  344. push(`</block>`);
  345. }
  346. }
  347. function checkVirtualHostProps(name, virtualHost) {
  348. const names = [name];
  349. if (virtualHost) {
  350. const obj = {
  351. style: utils_1.VIRTUAL_HOST_STYLE,
  352. class: utils_1.VIRTUAL_HOST_CLASS,
  353. };
  354. if (name in obj) {
  355. // TODO 支付宝平台移除原有属性(支付宝小程序自定义组件外部属性始终无效)
  356. names.push(obj[name]);
  357. }
  358. return names;
  359. }
  360. return names;
  361. }
  362. function genElementProps(node, virtualHost, context) {
  363. node.props.forEach((prop) => {
  364. if (prop.type === 6 /* NodeTypes.ATTRIBUTE */) {
  365. const { value } = prop;
  366. if (value) {
  367. checkVirtualHostProps(prop.name, virtualHost).forEach((name) => {
  368. context.push(` ${name}="${value.content}"`);
  369. });
  370. }
  371. else {
  372. context.push(` ${prop.name}`);
  373. }
  374. }
  375. else {
  376. const { name } = prop;
  377. if (name === 'on') {
  378. genOn(prop, node, context);
  379. }
  380. else {
  381. genDirectiveNode(prop, node, virtualHost, context);
  382. }
  383. }
  384. });
  385. }
  386. exports.genElementProps = genElementProps;
  387. function genOn(prop, node, { push, event, isBuiltInComponent }) {
  388. const arg = prop.arg.content;
  389. const exp = prop.exp;
  390. const modifiers = prop.modifiers;
  391. const name = (event?.format || uni_cli_shared_1.formatMiniProgramEvent)(arg, {
  392. isCatch: modifiers.includes('stop') || modifiers.includes('prevent'),
  393. isCapture: modifiers.includes('capture'),
  394. isComponent: (0, uni_cli_shared_1.isUserComponent)(node, { isBuiltInComponent }),
  395. });
  396. if (exp.isStatic) {
  397. push(` ${name}="${exp.content}"`);
  398. }
  399. else {
  400. push(` ${name}="{{${exp.content}}}"`);
  401. }
  402. }
  403. function genDirectiveNode(prop, node, virtualHost, context) {
  404. const { push, component } = context;
  405. if (prop.name === 'slot') {
  406. if (prop.arg) {
  407. const arg = prop.arg;
  408. if (arg.isStatic) {
  409. const slotName = (0, uni_shared_1.dynamicSlotName)(arg.content);
  410. // 非作用域默认插槽不生成 slot 属性
  411. if (slotName !== uni_shared_1.SLOT_DEFAULT_NAME) {
  412. push(` slot="${slotName}"`);
  413. }
  414. }
  415. else {
  416. push(` slot="{{${arg.content}}}"`);
  417. }
  418. }
  419. }
  420. else if (prop.name === 'show') {
  421. let hiddenPropName = 'hidden';
  422. if ((0, uni_cli_shared_1.isUserComponent)(node, context) && component && component.vShow) {
  423. hiddenPropName = component.vShow;
  424. }
  425. push(` ${hiddenPropName}="{{!${prop.exp.content}}}"`);
  426. }
  427. else if (prop.arg && prop.exp) {
  428. const arg = prop.arg.content;
  429. const exp = prop.exp.content;
  430. checkVirtualHostProps(arg, virtualHost).forEach((arg) => {
  431. push(` ${arg}="{{${exp}}}"`);
  432. });
  433. }
  434. else {
  435. if (prop.name !== 'bind') {
  436. throw new Error(`unknown directive ` + JSON.stringify(prop));
  437. }
  438. }
  439. }