index.js 12 KB

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