index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var _t = require('@babel/types');
  4. function _interopNamespace(e) {
  5. if (e && e.__esModule) return e;
  6. var n = Object.create(null);
  7. if (e) {
  8. Object.keys(e).forEach(function (k) {
  9. if (k !== 'default') {
  10. var d = Object.getOwnPropertyDescriptor(e, k);
  11. Object.defineProperty(n, k, d.get ? d : {
  12. enumerable: true,
  13. get: function () {
  14. return e[k];
  15. }
  16. });
  17. }
  18. });
  19. }
  20. n['default'] = e;
  21. return Object.freeze(n);
  22. }
  23. var _t__namespace = /*#__PURE__*/_interopNamespace(_t);
  24. function willPathCastToBoolean(path) {
  25. const maybeWrapped = path;
  26. const {
  27. node,
  28. parentPath
  29. } = maybeWrapped;
  30. if (parentPath.isLogicalExpression()) {
  31. const {
  32. operator,
  33. right
  34. } = parentPath.node;
  35. if (operator === "&&" || operator === "||" || operator === "??" && node === right) {
  36. return willPathCastToBoolean(parentPath);
  37. }
  38. }
  39. if (parentPath.isSequenceExpression()) {
  40. const {
  41. expressions
  42. } = parentPath.node;
  43. if (expressions[expressions.length - 1] === node) {
  44. return willPathCastToBoolean(parentPath);
  45. } else {
  46. return true;
  47. }
  48. }
  49. return parentPath.isConditional({
  50. test: node
  51. }) || parentPath.isUnaryExpression({
  52. operator: "!"
  53. }) || parentPath.isLoop({
  54. test: node
  55. });
  56. }
  57. const {
  58. LOGICAL_OPERATORS,
  59. arrowFunctionExpression,
  60. assignmentExpression,
  61. binaryExpression,
  62. booleanLiteral,
  63. callExpression,
  64. cloneNode,
  65. conditionalExpression,
  66. identifier,
  67. isMemberExpression,
  68. isOptionalCallExpression,
  69. isOptionalMemberExpression,
  70. isUpdateExpression,
  71. logicalExpression,
  72. memberExpression,
  73. nullLiteral,
  74. numericLiteral,
  75. optionalCallExpression,
  76. optionalMemberExpression,
  77. sequenceExpression,
  78. unaryExpression
  79. } = _t__namespace;
  80. class AssignmentMemoiser {
  81. constructor() {
  82. this._map = void 0;
  83. this._map = new WeakMap();
  84. }
  85. has(key) {
  86. return this._map.has(key);
  87. }
  88. get(key) {
  89. if (!this.has(key)) return;
  90. const record = this._map.get(key);
  91. const {
  92. value
  93. } = record;
  94. record.count--;
  95. if (record.count === 0) {
  96. return assignmentExpression("=", value, key);
  97. }
  98. return value;
  99. }
  100. set(key, value, count) {
  101. return this._map.set(key, {
  102. count,
  103. value
  104. });
  105. }
  106. }
  107. function toNonOptional(path, base) {
  108. const {
  109. node
  110. } = path;
  111. if (isOptionalMemberExpression(node)) {
  112. return memberExpression(base, node.property, node.computed);
  113. }
  114. if (path.isOptionalCallExpression()) {
  115. const callee = path.get("callee");
  116. if (path.node.optional && callee.isOptionalMemberExpression()) {
  117. const {
  118. object
  119. } = callee.node;
  120. const context = path.scope.maybeGenerateMemoised(object) || object;
  121. callee.get("object").replaceWith(assignmentExpression("=", context, object));
  122. return callExpression(memberExpression(base, identifier("call")), [context, ...path.node.arguments]);
  123. }
  124. return callExpression(base, path.node.arguments);
  125. }
  126. return path.node;
  127. }
  128. function isInDetachedTree(path) {
  129. while (path) {
  130. if (path.isProgram()) break;
  131. const {
  132. parentPath,
  133. container,
  134. listKey
  135. } = path;
  136. const parentNode = parentPath.node;
  137. if (listKey) {
  138. if (container !== parentNode[listKey]) return true;
  139. } else {
  140. if (container !== parentNode) return true;
  141. }
  142. path = parentPath;
  143. }
  144. return false;
  145. }
  146. const handle = {
  147. memoise() {},
  148. handle(member, noDocumentAll) {
  149. const {
  150. node,
  151. parent,
  152. parentPath,
  153. scope
  154. } = member;
  155. if (member.isOptionalMemberExpression()) {
  156. if (isInDetachedTree(member)) return;
  157. const endPath = member.find(({
  158. node,
  159. parent
  160. }) => {
  161. if (isOptionalMemberExpression(parent)) {
  162. return parent.optional || parent.object !== node;
  163. }
  164. if (isOptionalCallExpression(parent)) {
  165. return node !== member.node && parent.optional || parent.callee !== node;
  166. }
  167. return true;
  168. });
  169. if (scope.path.isPattern()) {
  170. endPath.replaceWith(callExpression(arrowFunctionExpression([], endPath.node), []));
  171. return;
  172. }
  173. const willEndPathCastToBoolean = willPathCastToBoolean(endPath);
  174. const rootParentPath = endPath.parentPath;
  175. if (rootParentPath.isUpdateExpression({
  176. argument: node
  177. }) || rootParentPath.isAssignmentExpression({
  178. left: node
  179. })) {
  180. throw member.buildCodeFrameError(`can't handle assignment`);
  181. }
  182. const isDeleteOperation = rootParentPath.isUnaryExpression({
  183. operator: "delete"
  184. });
  185. if (isDeleteOperation && endPath.isOptionalMemberExpression() && endPath.get("property").isPrivateName()) {
  186. throw member.buildCodeFrameError(`can't delete a private class element`);
  187. }
  188. let startingOptional = member;
  189. for (;;) {
  190. if (startingOptional.isOptionalMemberExpression()) {
  191. if (startingOptional.node.optional) break;
  192. startingOptional = startingOptional.get("object");
  193. continue;
  194. } else if (startingOptional.isOptionalCallExpression()) {
  195. if (startingOptional.node.optional) break;
  196. startingOptional = startingOptional.get("callee");
  197. continue;
  198. }
  199. throw new Error(`Internal error: unexpected ${startingOptional.node.type}`);
  200. }
  201. const startingProp = startingOptional.isOptionalMemberExpression() ? "object" : "callee";
  202. const startingNode = startingOptional.node[startingProp];
  203. const baseNeedsMemoised = scope.maybeGenerateMemoised(startingNode);
  204. const baseRef = baseNeedsMemoised != null ? baseNeedsMemoised : startingNode;
  205. const parentIsOptionalCall = parentPath.isOptionalCallExpression({
  206. callee: node
  207. });
  208. const isOptionalCall = parent => parentIsOptionalCall;
  209. const parentIsCall = parentPath.isCallExpression({
  210. callee: node
  211. });
  212. startingOptional.replaceWith(toNonOptional(startingOptional, baseRef));
  213. if (isOptionalCall()) {
  214. if (parent.optional) {
  215. parentPath.replaceWith(this.optionalCall(member, parent.arguments));
  216. } else {
  217. parentPath.replaceWith(this.call(member, parent.arguments));
  218. }
  219. } else if (parentIsCall) {
  220. member.replaceWith(this.boundGet(member));
  221. } else {
  222. member.replaceWith(this.get(member));
  223. }
  224. let regular = member.node;
  225. for (let current = member; current !== endPath;) {
  226. const parentPath = current.parentPath;
  227. if (parentPath === endPath && isOptionalCall() && parent.optional) {
  228. regular = parentPath.node;
  229. break;
  230. }
  231. regular = toNonOptional(parentPath, regular);
  232. current = parentPath;
  233. }
  234. let context;
  235. const endParentPath = endPath.parentPath;
  236. if (isMemberExpression(regular) && endParentPath.isOptionalCallExpression({
  237. callee: endPath.node,
  238. optional: true
  239. })) {
  240. const {
  241. object
  242. } = regular;
  243. context = member.scope.maybeGenerateMemoised(object);
  244. if (context) {
  245. regular.object = assignmentExpression("=", context, object);
  246. }
  247. }
  248. let replacementPath = endPath;
  249. if (isDeleteOperation) {
  250. replacementPath = endParentPath;
  251. regular = endParentPath.node;
  252. }
  253. const baseMemoised = baseNeedsMemoised ? assignmentExpression("=", cloneNode(baseRef), cloneNode(startingNode)) : cloneNode(baseRef);
  254. if (willEndPathCastToBoolean) {
  255. let nonNullishCheck;
  256. if (noDocumentAll) {
  257. nonNullishCheck = binaryExpression("!=", baseMemoised, nullLiteral());
  258. } else {
  259. nonNullishCheck = logicalExpression("&&", binaryExpression("!==", baseMemoised, nullLiteral()), binaryExpression("!==", cloneNode(baseRef), scope.buildUndefinedNode()));
  260. }
  261. replacementPath.replaceWith(logicalExpression("&&", nonNullishCheck, regular));
  262. } else {
  263. let nullishCheck;
  264. if (noDocumentAll) {
  265. nullishCheck = binaryExpression("==", baseMemoised, nullLiteral());
  266. } else {
  267. nullishCheck = logicalExpression("||", binaryExpression("===", baseMemoised, nullLiteral()), binaryExpression("===", cloneNode(baseRef), scope.buildUndefinedNode()));
  268. }
  269. replacementPath.replaceWith(conditionalExpression(nullishCheck, isDeleteOperation ? booleanLiteral(true) : scope.buildUndefinedNode(), regular));
  270. }
  271. if (context) {
  272. const endParent = endParentPath.node;
  273. endParentPath.replaceWith(optionalCallExpression(optionalMemberExpression(endParent.callee, identifier("call"), false, true), [cloneNode(context), ...endParent.arguments], false));
  274. }
  275. return;
  276. }
  277. if (isUpdateExpression(parent, {
  278. argument: node
  279. })) {
  280. if (this.simpleSet) {
  281. member.replaceWith(this.simpleSet(member));
  282. return;
  283. }
  284. const {
  285. operator,
  286. prefix
  287. } = parent;
  288. this.memoise(member, 2);
  289. const value = binaryExpression(operator[0], unaryExpression("+", this.get(member)), numericLiteral(1));
  290. if (prefix) {
  291. parentPath.replaceWith(this.set(member, value));
  292. } else {
  293. const {
  294. scope
  295. } = member;
  296. const ref = scope.generateUidIdentifierBasedOnNode(node);
  297. scope.push({
  298. id: ref
  299. });
  300. value.left = assignmentExpression("=", cloneNode(ref), value.left);
  301. parentPath.replaceWith(sequenceExpression([this.set(member, value), cloneNode(ref)]));
  302. }
  303. return;
  304. }
  305. if (parentPath.isAssignmentExpression({
  306. left: node
  307. })) {
  308. if (this.simpleSet) {
  309. member.replaceWith(this.simpleSet(member));
  310. return;
  311. }
  312. const {
  313. operator,
  314. right: value
  315. } = parentPath.node;
  316. if (operator === "=") {
  317. parentPath.replaceWith(this.set(member, value));
  318. } else {
  319. const operatorTrunc = operator.slice(0, -1);
  320. if (LOGICAL_OPERATORS.includes(operatorTrunc)) {
  321. this.memoise(member, 1);
  322. parentPath.replaceWith(logicalExpression(operatorTrunc, this.get(member), this.set(member, value)));
  323. } else {
  324. this.memoise(member, 2);
  325. parentPath.replaceWith(this.set(member, binaryExpression(operatorTrunc, this.get(member), value)));
  326. }
  327. }
  328. return;
  329. }
  330. if (parentPath.isCallExpression({
  331. callee: node
  332. })) {
  333. parentPath.replaceWith(this.call(member, parentPath.node.arguments));
  334. return;
  335. }
  336. if (parentPath.isOptionalCallExpression({
  337. callee: node
  338. })) {
  339. if (scope.path.isPattern()) {
  340. parentPath.replaceWith(callExpression(arrowFunctionExpression([], parentPath.node), []));
  341. return;
  342. }
  343. parentPath.replaceWith(this.optionalCall(member, parentPath.node.arguments));
  344. return;
  345. }
  346. if (parentPath.isForXStatement({
  347. left: node
  348. }) || parentPath.isObjectProperty({
  349. value: node
  350. }) && parentPath.parentPath.isObjectPattern() || parentPath.isAssignmentPattern({
  351. left: node
  352. }) && parentPath.parentPath.isObjectProperty({
  353. value: parent
  354. }) && parentPath.parentPath.parentPath.isObjectPattern() || parentPath.isArrayPattern() || parentPath.isAssignmentPattern({
  355. left: node
  356. }) && parentPath.parentPath.isArrayPattern() || parentPath.isRestElement()) {
  357. member.replaceWith(this.destructureSet(member));
  358. return;
  359. }
  360. if (parentPath.isTaggedTemplateExpression()) {
  361. member.replaceWith(this.boundGet(member));
  362. } else {
  363. member.replaceWith(this.get(member));
  364. }
  365. }
  366. };
  367. function memberExpressionToFunctions(path, visitor, state) {
  368. path.traverse(visitor, Object.assign({}, handle, state, {
  369. memoiser: new AssignmentMemoiser()
  370. }));
  371. }
  372. exports.default = memberExpressionToFunctions;
  373. //# sourceMappingURL=index.js.map