buildProps.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  10. Object.defineProperty(o, "default", { enumerable: true, value: v });
  11. }) : function(o, v) {
  12. o["default"] = v;
  13. });
  14. var __importStar = (this && this.__importStar) || function (mod) {
  15. if (mod && mod.__esModule) return mod;
  16. var result = {};
  17. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  18. __setModuleDefault(result, mod);
  19. return result;
  20. };
  21. var __importDefault = (this && this.__importDefault) || function (mod) {
  22. return (mod && mod.__esModule) ? mod : { "default": mod };
  23. };
  24. Object.defineProperty(exports, "__esModule", { value: true });
  25. const t = __importStar(require("@babel/types"));
  26. const helper_module_imports_1 = require("@babel/helper-module-imports");
  27. const utils_1 = require("./utils");
  28. const parseDirectives_1 = __importDefault(require("./parseDirectives"));
  29. const transform_vue_jsx_1 = require("./transform-vue-jsx");
  30. const xlinkRE = /^xlink([A-Z])/;
  31. const onRE = /^on[^a-z]/;
  32. const isOn = (key) => onRE.test(key);
  33. const getJSXAttributeValue = (path, state) => {
  34. const valuePath = path.get('value');
  35. if (valuePath.isJSXElement()) {
  36. return transform_vue_jsx_1.transformJSXElement(valuePath, state);
  37. }
  38. if (valuePath.isStringLiteral()) {
  39. return valuePath.node;
  40. }
  41. if (valuePath.isJSXExpressionContainer()) {
  42. return utils_1.transformJSXExpressionContainer(valuePath);
  43. }
  44. return null;
  45. };
  46. const transformJSXSpreadAttribute = (nodePath, path, mergeProps, args) => {
  47. const argument = path.get('argument');
  48. const properties = t.isObjectExpression(argument.node) ? argument.node.properties : undefined;
  49. if (!properties) {
  50. if (argument.isIdentifier()) {
  51. utils_1.walksScope(nodePath, argument.node.name, 2 /* DYNAMIC */);
  52. }
  53. args.push(mergeProps ? argument.node : t.spreadElement(argument.node));
  54. }
  55. else if (mergeProps) {
  56. args.push(t.objectExpression(properties));
  57. }
  58. else {
  59. args.push(...properties);
  60. }
  61. };
  62. const mergeAsArray = (existing, incoming) => {
  63. if (t.isArrayExpression(existing.value)) {
  64. existing.value.elements.push(incoming.value);
  65. }
  66. else {
  67. existing.value = t.arrayExpression([
  68. existing.value,
  69. incoming.value,
  70. ]);
  71. }
  72. };
  73. const dedupeProperties = (properties = [], mergeProps) => {
  74. if (!mergeProps) {
  75. return properties;
  76. }
  77. const knownProps = new Map();
  78. const deduped = [];
  79. properties.forEach((prop) => {
  80. if (t.isStringLiteral(prop.key)) {
  81. const { value: name } = prop.key;
  82. const existing = knownProps.get(name);
  83. if (existing) {
  84. if (name === 'style' || name === 'class' || name.startsWith('on')) {
  85. mergeAsArray(existing, prop);
  86. }
  87. }
  88. else {
  89. knownProps.set(name, prop);
  90. deduped.push(prop);
  91. }
  92. }
  93. else {
  94. // v-model target with variable
  95. deduped.push(prop);
  96. }
  97. });
  98. return deduped;
  99. };
  100. /**
  101. * Check if an attribute value is constant
  102. * @param node
  103. * @returns boolean
  104. */
  105. const isConstant = (node) => {
  106. if (t.isIdentifier(node)) {
  107. return node.name === 'undefined';
  108. }
  109. if (t.isArrayExpression(node)) {
  110. const { elements } = node;
  111. return elements.every((element) => element && isConstant(element));
  112. }
  113. if (t.isObjectExpression(node)) {
  114. return node.properties.every((property) => isConstant(property.value));
  115. }
  116. if (t.isLiteral(node)) {
  117. return true;
  118. }
  119. return false;
  120. };
  121. const buildProps = (path, state) => {
  122. const tag = utils_1.getTag(path, state);
  123. const isComponent = utils_1.checkIsComponent(path.get('openingElement'));
  124. const props = path.get('openingElement').get('attributes');
  125. const directives = [];
  126. const dynamicPropNames = new Set();
  127. let slots = null;
  128. let patchFlag = 0;
  129. if (props.length === 0) {
  130. return {
  131. tag,
  132. isComponent,
  133. slots,
  134. props: t.nullLiteral(),
  135. directives,
  136. patchFlag,
  137. dynamicPropNames,
  138. };
  139. }
  140. let properties = [];
  141. // patchFlag analysis
  142. let hasRef = false;
  143. let hasClassBinding = false;
  144. let hasStyleBinding = false;
  145. let hasHydrationEventBinding = false;
  146. let hasDynamicKeys = false;
  147. const mergeArgs = [];
  148. const { mergeProps = true } = state.opts;
  149. props
  150. .forEach((prop) => {
  151. if (prop.isJSXAttribute()) {
  152. let name = utils_1.getJSXAttributeName(prop);
  153. const attributeValue = getJSXAttributeValue(prop, state);
  154. if (!isConstant(attributeValue) || name === 'ref') {
  155. if (!isComponent
  156. && isOn(name)
  157. // omit the flag for click handlers becaues hydration gives click
  158. // dedicated fast path.
  159. && name.toLowerCase() !== 'onclick'
  160. // omit v-model handlers
  161. && name !== 'onUpdate:modelValue') {
  162. hasHydrationEventBinding = true;
  163. }
  164. if (name === 'ref') {
  165. hasRef = true;
  166. }
  167. else if (name === 'class' && !isComponent) {
  168. hasClassBinding = true;
  169. }
  170. else if (name === 'style' && !isComponent) {
  171. hasStyleBinding = true;
  172. }
  173. else if (name !== 'key'
  174. && !utils_1.isDirective(name)
  175. && name !== 'on') {
  176. dynamicPropNames.add(name);
  177. }
  178. }
  179. if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
  180. if (!state.get('transformOn')) {
  181. state.set('transformOn', helper_module_imports_1.addDefault(path, '@vue/babel-helper-vue-transform-on', { nameHint: '_transformOn' }));
  182. }
  183. mergeArgs.push(t.callExpression(state.get('transformOn'), [attributeValue || t.booleanLiteral(true)]));
  184. return;
  185. }
  186. if (utils_1.isDirective(name)) {
  187. const { directive, modifiers, values, args, directiveName, } = parseDirectives_1.default({
  188. tag,
  189. isComponent,
  190. name,
  191. path: prop,
  192. state,
  193. value: attributeValue,
  194. });
  195. if (directiveName === 'slots') {
  196. slots = attributeValue;
  197. return;
  198. }
  199. if (directive) {
  200. directives.push(t.arrayExpression(directive));
  201. }
  202. else if (directiveName === 'html') {
  203. properties.push(t.objectProperty(t.stringLiteral('innerHTML'), values[0]));
  204. dynamicPropNames.add('innerHTML');
  205. }
  206. else if (directiveName === 'text') {
  207. properties.push(t.objectProperty(t.stringLiteral('textContent'), values[0]));
  208. dynamicPropNames.add('textContent');
  209. }
  210. if (['models', 'model'].includes(directiveName)) {
  211. values.forEach((value, index) => {
  212. var _a, _b, _c, _d;
  213. const propName = args[index];
  214. // v-model target with variable
  215. // const isIdentifierProp = t.isIdentifier(propName);
  216. const isDynamic = !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
  217. // must be v-model or v-models and is a component
  218. if (!directive) {
  219. properties.push(t.objectProperty(t.isNullLiteral(propName)
  220. ? t.stringLiteral('modelValue') : propName, value, isDynamic));
  221. if (!isDynamic) {
  222. dynamicPropNames.add(((_a = propName) === null || _a === void 0 ? void 0 : _a.value) || 'modelValue');
  223. }
  224. if ((_b = modifiers[index]) === null || _b === void 0 ? void 0 : _b.size) {
  225. properties.push(t.objectProperty(isDynamic
  226. ? t.binaryExpression('+', propName, t.stringLiteral('Modifiers'))
  227. : t.stringLiteral(`${((_c = propName) === null || _c === void 0 ? void 0 : _c.value) || 'model'}Modifiers`), t.objectExpression([...modifiers[index]].map((modifier) => t.objectProperty(t.stringLiteral(modifier), t.booleanLiteral(true)))), isDynamic));
  228. }
  229. }
  230. const updateName = isDynamic
  231. ? t.binaryExpression('+', t.stringLiteral('onUpdate'), propName)
  232. : t.stringLiteral(`onUpdate:${((_d = propName) === null || _d === void 0 ? void 0 : _d.value) || 'modelValue'}`);
  233. properties.push(t.objectProperty(updateName, t.arrowFunctionExpression([t.identifier('$event')], t.assignmentExpression('=', value, t.identifier('$event'))), isDynamic));
  234. if (!isDynamic) {
  235. dynamicPropNames.add(updateName.value);
  236. }
  237. else {
  238. hasDynamicKeys = true;
  239. }
  240. });
  241. }
  242. }
  243. else {
  244. if (name.match(xlinkRE)) {
  245. name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
  246. }
  247. properties.push(t.objectProperty(t.stringLiteral(name), attributeValue || t.booleanLiteral(true)));
  248. }
  249. }
  250. else {
  251. if (properties.length && mergeProps) {
  252. mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
  253. properties = [];
  254. }
  255. // JSXSpreadAttribute
  256. hasDynamicKeys = true;
  257. transformJSXSpreadAttribute(path, prop, mergeProps, mergeProps ? mergeArgs : properties);
  258. }
  259. });
  260. // patchFlag analysis
  261. if (hasDynamicKeys) {
  262. patchFlag |= 16 /* FULL_PROPS */;
  263. }
  264. else {
  265. if (hasClassBinding) {
  266. patchFlag |= 2 /* CLASS */;
  267. }
  268. if (hasStyleBinding) {
  269. patchFlag |= 4 /* STYLE */;
  270. }
  271. if (dynamicPropNames.size) {
  272. patchFlag |= 8 /* PROPS */;
  273. }
  274. if (hasHydrationEventBinding) {
  275. patchFlag |= 32 /* HYDRATE_EVENTS */;
  276. }
  277. }
  278. if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */)
  279. && (hasRef || directives.length > 0)) {
  280. patchFlag |= 512 /* NEED_PATCH */;
  281. }
  282. let propsExpression = t.nullLiteral();
  283. if (mergeArgs.length) {
  284. if (properties.length) {
  285. mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
  286. }
  287. if (mergeArgs.length > 1) {
  288. propsExpression = t.callExpression(utils_1.createIdentifier(state, 'mergeProps'), mergeArgs);
  289. }
  290. else {
  291. // single no need for a mergeProps call
  292. propsExpression = mergeArgs[0];
  293. }
  294. }
  295. else if (properties.length) {
  296. // single no need for spread
  297. if (properties.length === 1 && t.isSpreadElement(properties[0])) {
  298. propsExpression = properties[0].argument;
  299. }
  300. else {
  301. propsExpression = t.objectExpression(dedupeProperties(properties, mergeProps));
  302. }
  303. }
  304. return {
  305. tag,
  306. props: propsExpression,
  307. isComponent,
  308. slots,
  309. directives,
  310. patchFlag,
  311. dynamicPropNames,
  312. };
  313. };
  314. exports.default = buildProps;