buildProps.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 } = argument.node;
  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 {
  56. args.push(t.objectExpression(properties));
  57. }
  58. };
  59. const mergeAsArray = (existing, incoming) => {
  60. if (t.isArrayExpression(existing.value)) {
  61. existing.value.elements.push(incoming.value);
  62. }
  63. else {
  64. existing.value = t.arrayExpression([
  65. existing.value,
  66. incoming.value,
  67. ]);
  68. }
  69. };
  70. const dedupeProperties = (properties = [], mergeProps) => {
  71. if (!mergeProps) {
  72. return properties;
  73. }
  74. const knownProps = new Map();
  75. const deduped = [];
  76. properties.forEach((prop) => {
  77. const { value: name } = prop.key;
  78. const existing = knownProps.get(name);
  79. if (existing) {
  80. if (name === 'style' || name === 'class' || name.startsWith('on')) {
  81. mergeAsArray(existing, prop);
  82. }
  83. }
  84. else {
  85. knownProps.set(name, prop);
  86. deduped.push(prop);
  87. }
  88. });
  89. return deduped;
  90. };
  91. /**
  92. * Check if an attribute value is constant
  93. * @param node
  94. * @returns boolean
  95. */
  96. const isConstant = (node) => {
  97. if (t.isIdentifier(node)) {
  98. return node.name === 'undefined';
  99. }
  100. if (t.isArrayExpression(node)) {
  101. const { elements } = node;
  102. return elements.every((element) => element && isConstant(element));
  103. }
  104. if (t.isObjectExpression(node)) {
  105. return node.properties.every((property) => isConstant(property.value));
  106. }
  107. if (t.isLiteral(node)) {
  108. return true;
  109. }
  110. return false;
  111. };
  112. const buildProps = (path, state) => {
  113. const tag = utils_1.getTag(path, state);
  114. const isComponent = utils_1.checkIsComponent(path.get('openingElement'));
  115. const props = path.get('openingElement').get('attributes');
  116. const directives = [];
  117. const dynamicPropNames = new Set();
  118. let slots = null;
  119. let patchFlag = 0;
  120. if (props.length === 0) {
  121. return {
  122. tag,
  123. isComponent,
  124. slots,
  125. props: t.nullLiteral(),
  126. directives,
  127. patchFlag,
  128. dynamicPropNames,
  129. };
  130. }
  131. let properties = [];
  132. // patchFlag analysis
  133. let hasRef = false;
  134. let hasClassBinding = false;
  135. let hasStyleBinding = false;
  136. let hasHydrationEventBinding = false;
  137. let hasDynamicKeys = false;
  138. const mergeArgs = [];
  139. const { mergeProps = true } = state.opts;
  140. props
  141. .forEach((prop) => {
  142. var _a;
  143. if (prop.isJSXAttribute()) {
  144. let name = utils_1.getJSXAttributeName(prop);
  145. const attributeValue = getJSXAttributeValue(prop, state);
  146. if (!isConstant(attributeValue) || name === 'ref') {
  147. if (!isComponent
  148. && isOn(name)
  149. // omit the flag for click handlers becaues hydration gives click
  150. // dedicated fast path.
  151. && name.toLowerCase() !== 'onclick'
  152. // omit v-model handlers
  153. && name !== 'onUpdate:modelValue') {
  154. hasHydrationEventBinding = true;
  155. }
  156. if (name === 'ref') {
  157. hasRef = true;
  158. }
  159. else if (name === 'class' && !isComponent) {
  160. hasClassBinding = true;
  161. }
  162. else if (name === 'style' && !isComponent) {
  163. hasStyleBinding = true;
  164. }
  165. else if (name !== 'key'
  166. && !utils_1.isDirective(name)
  167. && name !== 'on') {
  168. dynamicPropNames.add(name);
  169. }
  170. }
  171. if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
  172. if (!state.get('transformOn')) {
  173. state.set('transformOn', helper_module_imports_1.addDefault(path, '@vue/babel-helper-vue-transform-on', { nameHint: '_transformOn' }));
  174. }
  175. mergeArgs.push(t.callExpression(state.get('transformOn'), [attributeValue || t.booleanLiteral(true)]));
  176. return;
  177. }
  178. if (utils_1.isDirective(name)) {
  179. const { directive, modifiers, value, arg, directiveName, } = parseDirectives_1.default({
  180. tag,
  181. isComponent,
  182. name,
  183. path: prop,
  184. state,
  185. value: attributeValue,
  186. });
  187. const argVal = (_a = arg) === null || _a === void 0 ? void 0 : _a.value;
  188. const propName = argVal || 'modelValue';
  189. if (directiveName === 'slots') {
  190. slots = attributeValue;
  191. return;
  192. }
  193. if (directive) {
  194. directives.push(t.arrayExpression(directive));
  195. }
  196. else if (directiveName === 'model') {
  197. // must be v-model and is a component
  198. properties.push(t.objectProperty(arg || t.stringLiteral('modelValue'),
  199. // @ts-ignore
  200. value));
  201. dynamicPropNames.add(propName);
  202. if (modifiers.size) {
  203. properties.push(t.objectProperty(t.stringLiteral(`${argVal || 'model'}Modifiers`), t.objectExpression([...modifiers].map((modifier) => (t.objectProperty(t.stringLiteral(modifier), t.booleanLiteral(true)))))));
  204. }
  205. }
  206. else if (directiveName === 'html') {
  207. properties.push(t.objectProperty(t.stringLiteral('innerHTML'), value));
  208. dynamicPropNames.add('innerHTML');
  209. }
  210. else if (directiveName === 'text') {
  211. properties.push(t.objectProperty(t.stringLiteral('textContent'), value));
  212. dynamicPropNames.add('textContent');
  213. }
  214. if (directiveName === 'model' && value) {
  215. properties.push(t.objectProperty(t.stringLiteral(`onUpdate:${propName}`), t.arrowFunctionExpression([t.identifier('$event')],
  216. // @ts-ignore
  217. t.assignmentExpression('=', value, t.identifier('$event')))));
  218. dynamicPropNames.add(`onUpdate:${propName}`);
  219. }
  220. }
  221. else {
  222. if (name.match(xlinkRE)) {
  223. name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
  224. }
  225. properties.push(t.objectProperty(t.stringLiteral(name), attributeValue || t.booleanLiteral(true)));
  226. }
  227. }
  228. else {
  229. if (properties.length && mergeProps) {
  230. mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
  231. properties = [];
  232. }
  233. // JSXSpreadAttribute
  234. hasDynamicKeys = true;
  235. transformJSXSpreadAttribute(path, prop, mergeProps, mergeProps ? mergeArgs : properties);
  236. }
  237. });
  238. // patchFlag analysis
  239. if (hasDynamicKeys) {
  240. patchFlag |= 16 /* FULL_PROPS */;
  241. }
  242. else {
  243. if (hasClassBinding) {
  244. patchFlag |= 2 /* CLASS */;
  245. }
  246. if (hasStyleBinding) {
  247. patchFlag |= 4 /* STYLE */;
  248. }
  249. if (dynamicPropNames.size) {
  250. patchFlag |= 8 /* PROPS */;
  251. }
  252. if (hasHydrationEventBinding) {
  253. patchFlag |= 32 /* HYDRATE_EVENTS */;
  254. }
  255. }
  256. if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */)
  257. && (hasRef || directives.length > 0)) {
  258. patchFlag |= 512 /* NEED_PATCH */;
  259. }
  260. let propsExpression = t.nullLiteral();
  261. if (mergeArgs.length) {
  262. if (properties.length) {
  263. mergeArgs.push(t.objectExpression(dedupeProperties(properties, mergeProps)));
  264. }
  265. if (mergeArgs.length > 1) {
  266. propsExpression = t.callExpression(utils_1.createIdentifier(state, 'mergeProps'), mergeArgs);
  267. }
  268. else {
  269. // single no need for a mergeProps call
  270. propsExpression = mergeArgs[0];
  271. }
  272. }
  273. else if (properties.length) {
  274. // single no need for spread
  275. if (properties.length === 1 && t.isSpreadElement(properties[0])) {
  276. propsExpression = properties[0].argument;
  277. }
  278. else {
  279. propsExpression = t.objectExpression(dedupeProperties(properties, mergeProps));
  280. }
  281. }
  282. return {
  283. tag,
  284. props: propsExpression,
  285. isComponent,
  286. slots,
  287. directives,
  288. patchFlag,
  289. dynamicPropNames,
  290. };
  291. };
  292. exports.default = buildProps;