transform-vue-jsx.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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 xlinkRE = /^xlink([A-Z])/;
  30. const getJSXAttributeValue = (path, state) => {
  31. const valuePath = path.get('value');
  32. if (valuePath.isJSXElement()) {
  33. return transformJSXElement(valuePath, state);
  34. }
  35. if (valuePath.isStringLiteral()) {
  36. return valuePath.node;
  37. }
  38. if (valuePath.isJSXExpressionContainer()) {
  39. return (0, utils_1.transformJSXExpressionContainer)(valuePath);
  40. }
  41. return null;
  42. };
  43. const buildProps = (path, state) => {
  44. const tag = (0, utils_1.getTag)(path, state);
  45. const isComponent = (0, utils_1.checkIsComponent)(path.get('openingElement'), state);
  46. const props = path.get('openingElement').get('attributes');
  47. const directives = [];
  48. const dynamicPropNames = new Set();
  49. let slots = null;
  50. let patchFlag = 0;
  51. if (props.length === 0) {
  52. return {
  53. tag,
  54. isComponent,
  55. slots,
  56. props: t.nullLiteral(),
  57. directives,
  58. patchFlag,
  59. dynamicPropNames,
  60. };
  61. }
  62. let properties = [];
  63. // patchFlag analysis
  64. let hasRef = false;
  65. let hasClassBinding = false;
  66. let hasStyleBinding = false;
  67. let hasHydrationEventBinding = false;
  68. let hasDynamicKeys = false;
  69. const mergeArgs = [];
  70. const { mergeProps = true } = state.opts;
  71. props
  72. .forEach((prop) => {
  73. if (prop.isJSXAttribute()) {
  74. let name = (0, utils_1.getJSXAttributeName)(prop);
  75. const attributeValue = getJSXAttributeValue(prop, state);
  76. if (!(0, utils_1.isConstant)(attributeValue) || name === 'ref') {
  77. if (!isComponent
  78. && (0, utils_1.isOn)(name)
  79. // omit the flag for click handlers becaues hydration gives click
  80. // dedicated fast path.
  81. && name.toLowerCase() !== 'onclick'
  82. // omit v-model handlers
  83. && name !== 'onUpdate:modelValue') {
  84. hasHydrationEventBinding = true;
  85. }
  86. if (name === 'ref') {
  87. hasRef = true;
  88. }
  89. else if (name === 'class' && !isComponent) {
  90. hasClassBinding = true;
  91. }
  92. else if (name === 'style' && !isComponent) {
  93. hasStyleBinding = true;
  94. }
  95. else if (name !== 'key'
  96. && !(0, utils_1.isDirective)(name)
  97. && name !== 'on') {
  98. dynamicPropNames.add(name);
  99. }
  100. }
  101. if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
  102. if (!state.get('transformOn')) {
  103. state.set('transformOn', (0, helper_module_imports_1.addDefault)(path, '@vue/babel-helper-vue-transform-on', { nameHint: '_transformOn' }));
  104. }
  105. mergeArgs.push(t.callExpression(state.get('transformOn'), [attributeValue || t.booleanLiteral(true)]));
  106. return;
  107. }
  108. if ((0, utils_1.isDirective)(name)) {
  109. const { directive, modifiers, values, args, directiveName, } = (0, parseDirectives_1.default)({
  110. tag,
  111. isComponent,
  112. name,
  113. path: prop,
  114. state,
  115. value: attributeValue,
  116. });
  117. if (directiveName === 'slots') {
  118. slots = attributeValue;
  119. return;
  120. }
  121. if (directive) {
  122. directives.push(t.arrayExpression(directive));
  123. }
  124. else if (directiveName === 'html') {
  125. properties.push(t.objectProperty(t.stringLiteral('innerHTML'), values[0]));
  126. dynamicPropNames.add('innerHTML');
  127. }
  128. else if (directiveName === 'text') {
  129. properties.push(t.objectProperty(t.stringLiteral('textContent'), values[0]));
  130. dynamicPropNames.add('textContent');
  131. }
  132. if (['models', 'model'].includes(directiveName)) {
  133. values.forEach((value, index) => {
  134. var _a, _b, _c, _d;
  135. const propName = args[index];
  136. // v-model target with variable
  137. const isDynamic = propName && !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
  138. // must be v-model or v-models and is a component
  139. if (!directive) {
  140. properties.push(t.objectProperty(t.isNullLiteral(propName)
  141. ? t.stringLiteral('modelValue') : propName, value, isDynamic));
  142. if (!isDynamic) {
  143. dynamicPropNames.add(((_a = propName) === null || _a === void 0 ? void 0 : _a.value) || 'modelValue');
  144. }
  145. if ((_b = modifiers[index]) === null || _b === void 0 ? void 0 : _b.size) {
  146. properties.push(t.objectProperty(isDynamic
  147. ? t.binaryExpression('+', propName, t.stringLiteral('Modifiers'))
  148. : 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));
  149. }
  150. }
  151. const updateName = isDynamic
  152. ? t.binaryExpression('+', t.stringLiteral('onUpdate'), propName)
  153. : t.stringLiteral(`onUpdate:${((_d = propName) === null || _d === void 0 ? void 0 : _d.value) || 'modelValue'}`);
  154. properties.push(t.objectProperty(updateName, t.arrowFunctionExpression([t.identifier('$event')], t.assignmentExpression('=', value, t.identifier('$event'))), isDynamic));
  155. if (!isDynamic) {
  156. dynamicPropNames.add(updateName.value);
  157. }
  158. else {
  159. hasDynamicKeys = true;
  160. }
  161. });
  162. }
  163. }
  164. else {
  165. if (name.match(xlinkRE)) {
  166. name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
  167. }
  168. properties.push(t.objectProperty(t.stringLiteral(name), attributeValue || t.booleanLiteral(true)));
  169. }
  170. }
  171. else {
  172. if (properties.length && mergeProps) {
  173. mergeArgs.push(t.objectExpression((0, utils_1.dedupeProperties)(properties, mergeProps)));
  174. properties = [];
  175. }
  176. // JSXSpreadAttribute
  177. hasDynamicKeys = true;
  178. (0, utils_1.transformJSXSpreadAttribute)(path, prop, mergeProps, mergeProps ? mergeArgs : properties);
  179. }
  180. });
  181. // patchFlag analysis
  182. if (hasDynamicKeys) {
  183. patchFlag |= 16 /* FULL_PROPS */;
  184. }
  185. else {
  186. if (hasClassBinding) {
  187. patchFlag |= 2 /* CLASS */;
  188. }
  189. if (hasStyleBinding) {
  190. patchFlag |= 4 /* STYLE */;
  191. }
  192. if (dynamicPropNames.size) {
  193. patchFlag |= 8 /* PROPS */;
  194. }
  195. if (hasHydrationEventBinding) {
  196. patchFlag |= 32 /* HYDRATE_EVENTS */;
  197. }
  198. }
  199. if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */)
  200. && (hasRef || directives.length > 0)) {
  201. patchFlag |= 512 /* NEED_PATCH */;
  202. }
  203. let propsExpression = t.nullLiteral();
  204. if (mergeArgs.length) {
  205. if (properties.length) {
  206. mergeArgs.push(t.objectExpression((0, utils_1.dedupeProperties)(properties, mergeProps)));
  207. }
  208. if (mergeArgs.length > 1) {
  209. propsExpression = t.callExpression((0, utils_1.createIdentifier)(state, 'mergeProps'), mergeArgs);
  210. }
  211. else {
  212. // single no need for a mergeProps call
  213. propsExpression = mergeArgs[0];
  214. }
  215. }
  216. else if (properties.length) {
  217. // single no need for spread
  218. if (properties.length === 1 && t.isSpreadElement(properties[0])) {
  219. propsExpression = properties[0].argument;
  220. }
  221. else {
  222. propsExpression = t.objectExpression((0, utils_1.dedupeProperties)(properties, mergeProps));
  223. }
  224. }
  225. return {
  226. tag,
  227. props: propsExpression,
  228. isComponent,
  229. slots,
  230. directives,
  231. patchFlag,
  232. dynamicPropNames,
  233. };
  234. };
  235. /**
  236. * Get children from Array of JSX children
  237. * @param paths Array<JSXText | JSXExpressionContainer | JSXElement | JSXFragment>
  238. * @returns Array<Expression | SpreadElement>
  239. */
  240. const getChildren = (paths, state) => paths
  241. .map((path) => {
  242. if (path.isJSXText()) {
  243. const transformedText = (0, utils_1.transformJSXText)(path);
  244. if (transformedText) {
  245. return t.callExpression((0, utils_1.createIdentifier)(state, 'createTextVNode'), [transformedText]);
  246. }
  247. return transformedText;
  248. }
  249. if (path.isJSXExpressionContainer()) {
  250. const expression = (0, utils_1.transformJSXExpressionContainer)(path);
  251. if (t.isIdentifier(expression)) {
  252. const { name } = expression;
  253. const { referencePaths = [] } = path.scope.getBinding(name) || {};
  254. referencePaths.forEach((referencePath) => {
  255. (0, utils_1.walksScope)(referencePath, name, 2 /* DYNAMIC */);
  256. });
  257. }
  258. return expression;
  259. }
  260. if (t.isJSXSpreadChild(path)) {
  261. return (0, utils_1.transformJSXSpreadChild)(path);
  262. }
  263. if (path.isCallExpression()) {
  264. return path.node;
  265. }
  266. if (path.isJSXElement()) {
  267. return transformJSXElement(path, state);
  268. }
  269. throw new Error(`getChildren: ${path.type} is not supported`);
  270. }).filter(((value) => (value !== undefined
  271. && value !== null
  272. && !t.isJSXEmptyExpression(value))));
  273. const transformJSXElement = (path, state) => {
  274. const children = getChildren(path.get('children'), state);
  275. const { tag, props, isComponent, directives, patchFlag, dynamicPropNames, slots, } = buildProps(path, state);
  276. const { optimize = false } = state.opts;
  277. const slotFlag = path.getData('slotFlag') || 1 /* STABLE */;
  278. let VNodeChild;
  279. if (children.length > 1 || slots) {
  280. /*
  281. <A v-slots={slots}>{a}{b}</A>
  282. ---> {{ default: () => [a, b], ...slots }}
  283. ---> {[a, b]}
  284. */
  285. VNodeChild = isComponent
  286. ? children.length
  287. ? t.objectExpression([
  288. !!children.length && t.objectProperty(t.identifier('default'), t.arrowFunctionExpression([], t.arrayExpression((0, utils_1.buildIIFE)(path, children)))),
  289. ...(slots ? (t.isObjectExpression(slots)
  290. ? slots.properties
  291. : [t.spreadElement(slots)]) : []),
  292. optimize && t.objectProperty(t.identifier('_'), t.numericLiteral(slotFlag)),
  293. ].filter(Boolean))
  294. : slots
  295. : t.arrayExpression(children);
  296. }
  297. else if (children.length === 1) {
  298. /*
  299. <A>{a}</A> or <A>{() => a}</A>
  300. */
  301. const { enableObjectSlots = true } = state.opts;
  302. const child = children[0];
  303. const objectExpression = t.objectExpression([
  304. t.objectProperty(t.identifier('default'), t.arrowFunctionExpression([], t.arrayExpression((0, utils_1.buildIIFE)(path, [child])))),
  305. optimize && t.objectProperty(t.identifier('_'), t.numericLiteral(slotFlag)),
  306. ].filter(Boolean));
  307. if (t.isIdentifier(child) && isComponent) {
  308. VNodeChild = enableObjectSlots ? t.conditionalExpression(t.callExpression(state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(), [child]), child, objectExpression) : objectExpression;
  309. }
  310. else if (t.isCallExpression(child) && child.loc && isComponent) { // the element was generated and doesn't have location information
  311. if (enableObjectSlots) {
  312. const { scope } = path;
  313. const slotId = scope.generateUidIdentifier('slot');
  314. if (scope) {
  315. scope.push({
  316. id: slotId,
  317. kind: 'let',
  318. });
  319. }
  320. const alternate = t.objectExpression([
  321. t.objectProperty(t.identifier('default'), t.arrowFunctionExpression([], t.arrayExpression((0, utils_1.buildIIFE)(path, [slotId])))), optimize && t.objectProperty(t.identifier('_'), t.numericLiteral(slotFlag)),
  322. ].filter(Boolean));
  323. const assignment = t.assignmentExpression('=', slotId, child);
  324. const condition = t.callExpression(state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(), [assignment]);
  325. VNodeChild = t.conditionalExpression(condition, slotId, alternate);
  326. }
  327. else {
  328. VNodeChild = objectExpression;
  329. }
  330. }
  331. else if (t.isFunctionExpression(child) || t.isArrowFunctionExpression(child)) {
  332. VNodeChild = t.objectExpression([
  333. t.objectProperty(t.identifier('default'), child),
  334. ]);
  335. }
  336. else if (t.isObjectExpression(child)) {
  337. VNodeChild = t.objectExpression([
  338. ...child.properties,
  339. optimize && t.objectProperty(t.identifier('_'), t.numericLiteral(slotFlag)),
  340. ].filter(Boolean));
  341. }
  342. else {
  343. VNodeChild = isComponent ? t.objectExpression([
  344. t.objectProperty(t.identifier('default'), t.arrowFunctionExpression([], t.arrayExpression([child]))),
  345. ]) : t.arrayExpression([child]);
  346. }
  347. }
  348. const createVNode = t.callExpression((0, utils_1.createIdentifier)(state, 'createVNode'), [
  349. tag,
  350. props,
  351. VNodeChild || t.nullLiteral(),
  352. !!patchFlag && optimize && t.numericLiteral(patchFlag),
  353. !!dynamicPropNames.size && optimize
  354. && t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name))),
  355. ].filter(Boolean));
  356. if (!directives.length) {
  357. return createVNode;
  358. }
  359. return t.callExpression((0, utils_1.createIdentifier)(state, 'withDirectives'), [
  360. createVNode,
  361. t.arrayExpression(directives),
  362. ]);
  363. };
  364. exports.default = ({
  365. JSXElement: {
  366. exit(path, state) {
  367. path.replaceWith(transformJSXElement(path, state));
  368. },
  369. },
  370. });
  371. //# sourceMappingURL=transform-vue-jsx.js.map