index.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /*istanbul ignore next*/"use strict";
  2. exports.__esModule = true;
  3. var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
  4. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  5. var _getIterator2 = require("babel-runtime/core-js/get-iterator");
  6. var _getIterator3 = _interopRequireDefault(_getIterator2);
  7. exports.default = function ( /*istanbul ignore next*/_ref) {
  8. /*istanbul ignore next*/var t = _ref.types;
  9. /**
  10. * Test if a VariableDeclaration's declarations contains any Patterns.
  11. */
  12. function variableDeclarationHasPattern(node) {
  13. for ( /*istanbul ignore next*/var _iterator = node.declarations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : (0, _getIterator3.default)(_iterator);;) {
  14. /*istanbul ignore next*/
  15. var _ref2;
  16. if (_isArray) {
  17. if (_i >= _iterator.length) break;
  18. _ref2 = _iterator[_i++];
  19. } else {
  20. _i = _iterator.next();
  21. if (_i.done) break;
  22. _ref2 = _i.value;
  23. }
  24. var declar = _ref2;
  25. if (t.isPattern(declar.id)) {
  26. return true;
  27. }
  28. }
  29. return false;
  30. }
  31. /**
  32. * Test if an ArrayPattern's elements contain any RestElements.
  33. */
  34. function hasRest(pattern) {
  35. for ( /*istanbul ignore next*/var _iterator2 = pattern.elements, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : (0, _getIterator3.default)(_iterator2);;) {
  36. /*istanbul ignore next*/
  37. var _ref3;
  38. if (_isArray2) {
  39. if (_i2 >= _iterator2.length) break;
  40. _ref3 = _iterator2[_i2++];
  41. } else {
  42. _i2 = _iterator2.next();
  43. if (_i2.done) break;
  44. _ref3 = _i2.value;
  45. }
  46. var elem = _ref3;
  47. if (t.isRestElement(elem)) {
  48. return true;
  49. }
  50. }
  51. return false;
  52. }
  53. var arrayUnpackVisitor = { /*istanbul ignore next*/
  54. ReferencedIdentifier: function ReferencedIdentifier(path, state) {
  55. if (state.bindings[path.node.name]) {
  56. state.deopt = true;
  57. path.stop();
  58. }
  59. }
  60. };
  61. /*istanbul ignore next*/
  62. var DestructuringTransformer = function () {
  63. function /*istanbul ignore next*/DestructuringTransformer(opts) {
  64. /*istanbul ignore next*/(0, _classCallCheck3.default)(this, DestructuringTransformer);
  65. this.blockHoist = opts.blockHoist;
  66. this.operator = opts.operator;
  67. this.arrays = {};
  68. this.nodes = opts.nodes || [];
  69. this.scope = opts.scope;
  70. this.file = opts.file;
  71. this.kind = opts.kind;
  72. }
  73. DestructuringTransformer.prototype.buildVariableAssignment = function buildVariableAssignment(id, init) {
  74. var op = this.operator;
  75. if (t.isMemberExpression(id)) op = "=";
  76. var node = /*istanbul ignore next*/void 0;
  77. if (op) {
  78. node = t.expressionStatement(t.assignmentExpression(op, id, init));
  79. } else {
  80. node = t.variableDeclaration(this.kind, [t.variableDeclarator(id, init)]);
  81. }
  82. node._blockHoist = this.blockHoist;
  83. return node;
  84. };
  85. DestructuringTransformer.prototype.buildVariableDeclaration = function buildVariableDeclaration(id, init) {
  86. var declar = t.variableDeclaration("var", [t.variableDeclarator(id, init)]);
  87. declar._blockHoist = this.blockHoist;
  88. return declar;
  89. };
  90. DestructuringTransformer.prototype.push = function push(id, init) {
  91. if (t.isObjectPattern(id)) {
  92. this.pushObjectPattern(id, init);
  93. } else if (t.isArrayPattern(id)) {
  94. this.pushArrayPattern(id, init);
  95. } else if (t.isAssignmentPattern(id)) {
  96. this.pushAssignmentPattern(id, init);
  97. } else {
  98. this.nodes.push(this.buildVariableAssignment(id, init));
  99. }
  100. };
  101. DestructuringTransformer.prototype.toArray = function toArray(node, count) {
  102. if (this.file.opts.loose || t.isIdentifier(node) && this.arrays[node.name]) {
  103. return node;
  104. } else {
  105. return this.scope.toArray(node, count);
  106. }
  107. };
  108. DestructuringTransformer.prototype.pushAssignmentPattern = function pushAssignmentPattern(pattern, valueRef) {
  109. // we need to assign the current value of the assignment to avoid evaluating
  110. // it more than once
  111. var tempValueRef = this.scope.generateUidIdentifierBasedOnNode(valueRef);
  112. var declar = t.variableDeclaration("var", [t.variableDeclarator(tempValueRef, valueRef)]);
  113. declar._blockHoist = this.blockHoist;
  114. this.nodes.push(declar);
  115. //
  116. var tempConditional = t.conditionalExpression(t.binaryExpression("===", tempValueRef, t.identifier("undefined")), pattern.right, tempValueRef);
  117. var left = pattern.left;
  118. if (t.isPattern(left)) {
  119. var tempValueDefault = t.expressionStatement(t.assignmentExpression("=", tempValueRef, tempConditional));
  120. tempValueDefault._blockHoist = this.blockHoist;
  121. this.nodes.push(tempValueDefault);
  122. this.push(left, tempValueRef);
  123. } else {
  124. this.nodes.push(this.buildVariableAssignment(left, tempConditional));
  125. }
  126. };
  127. DestructuringTransformer.prototype.pushObjectRest = function pushObjectRest(pattern, objRef, spreadProp, spreadPropIndex) {
  128. // get all the keys that appear in this object before the current spread
  129. var keys = [];
  130. for (var i = 0; i < pattern.properties.length; i++) {
  131. var prop = pattern.properties[i];
  132. // we've exceeded the index of the spread property to all properties to the
  133. // right need to be ignored
  134. if (i >= spreadPropIndex) break;
  135. // ignore other spread properties
  136. if (t.isRestProperty(prop)) continue;
  137. var key = prop.key;
  138. if (t.isIdentifier(key) && !prop.computed) key = t.stringLiteral(prop.key.name);
  139. keys.push(key);
  140. }
  141. keys = t.arrayExpression(keys);
  142. //
  143. var value = t.callExpression(this.file.addHelper("objectWithoutProperties"), [objRef, keys]);
  144. this.nodes.push(this.buildVariableAssignment(spreadProp.argument, value));
  145. };
  146. DestructuringTransformer.prototype.pushObjectProperty = function pushObjectProperty(prop, propRef) {
  147. if (t.isLiteral(prop.key)) prop.computed = true;
  148. var pattern = prop.value;
  149. var objRef = t.memberExpression(propRef, prop.key, prop.computed);
  150. if (t.isPattern(pattern)) {
  151. this.push(pattern, objRef);
  152. } else {
  153. this.nodes.push(this.buildVariableAssignment(pattern, objRef));
  154. }
  155. };
  156. DestructuringTransformer.prototype.pushObjectPattern = function pushObjectPattern(pattern, objRef) {
  157. // https://github.com/babel/babel/issues/681
  158. if (!pattern.properties.length) {
  159. this.nodes.push(t.expressionStatement(t.callExpression(this.file.addHelper("objectDestructuringEmpty"), [objRef])));
  160. }
  161. // if we have more than one properties in this pattern and the objectRef is a
  162. // member expression then we need to assign it to a temporary variable so it's
  163. // only evaluated once
  164. if (pattern.properties.length > 1 && !this.scope.isStatic(objRef)) {
  165. var temp = this.scope.generateUidIdentifierBasedOnNode(objRef);
  166. this.nodes.push(this.buildVariableDeclaration(temp, objRef));
  167. objRef = temp;
  168. }
  169. //
  170. for (var i = 0; i < pattern.properties.length; i++) {
  171. var prop = pattern.properties[i];
  172. if (t.isRestProperty(prop)) {
  173. this.pushObjectRest(pattern, objRef, prop, i);
  174. } else {
  175. this.pushObjectProperty(prop, objRef);
  176. }
  177. }
  178. };
  179. DestructuringTransformer.prototype.canUnpackArrayPattern = function canUnpackArrayPattern(pattern, arr) {
  180. // not an array so there's no way we can deal with this
  181. if (!t.isArrayExpression(arr)) return false;
  182. // pattern has less elements than the array and doesn't have a rest so some
  183. // elements wont be evaluated
  184. if (pattern.elements.length > arr.elements.length) return;
  185. if (pattern.elements.length < arr.elements.length && !hasRest(pattern)) return false;
  186. for ( /*istanbul ignore next*/var _iterator3 = pattern.elements, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : (0, _getIterator3.default)(_iterator3);;) {
  187. /*istanbul ignore next*/
  188. var _ref4;
  189. if (_isArray3) {
  190. if (_i3 >= _iterator3.length) break;
  191. _ref4 = _iterator3[_i3++];
  192. } else {
  193. _i3 = _iterator3.next();
  194. if (_i3.done) break;
  195. _ref4 = _i3.value;
  196. }
  197. var elem = _ref4;
  198. // deopt on holes
  199. if (!elem) return false;
  200. // deopt on member expressions as they may be included in the RHS
  201. if (t.isMemberExpression(elem)) return false;
  202. }
  203. for ( /*istanbul ignore next*/var _iterator4 = arr.elements, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : (0, _getIterator3.default)(_iterator4);;) {
  204. /*istanbul ignore next*/
  205. var _ref5;
  206. if (_isArray4) {
  207. if (_i4 >= _iterator4.length) break;
  208. _ref5 = _iterator4[_i4++];
  209. } else {
  210. _i4 = _iterator4.next();
  211. if (_i4.done) break;
  212. _ref5 = _i4.value;
  213. }
  214. var _elem = _ref5;
  215. // deopt on spread elements
  216. if (t.isSpreadElement(_elem)) return false;
  217. }
  218. // deopt on reference to left side identifiers
  219. var bindings = t.getBindingIdentifiers(pattern);
  220. var state = { deopt: false, bindings: bindings };
  221. this.scope.traverse(arr, arrayUnpackVisitor, state);
  222. return !state.deopt;
  223. };
  224. DestructuringTransformer.prototype.pushUnpackedArrayPattern = function pushUnpackedArrayPattern(pattern, arr) {
  225. for (var i = 0; i < pattern.elements.length; i++) {
  226. var elem = pattern.elements[i];
  227. if (t.isRestElement(elem)) {
  228. this.push(elem.argument, t.arrayExpression(arr.elements.slice(i)));
  229. } else {
  230. this.push(elem, arr.elements[i]);
  231. }
  232. }
  233. };
  234. DestructuringTransformer.prototype.pushArrayPattern = function pushArrayPattern(pattern, arrayRef) {
  235. if (!pattern.elements) return;
  236. // optimise basic array destructuring of an array expression
  237. //
  238. // we can't do this to a pattern of unequal size to it's right hand
  239. // array expression as then there will be values that wont be evaluated
  240. //
  241. // eg: let [a, b] = [1, 2];
  242. if (this.canUnpackArrayPattern(pattern, arrayRef)) {
  243. return this.pushUnpackedArrayPattern(pattern, arrayRef);
  244. }
  245. // if we have a rest then we need all the elements so don't tell
  246. // `scope.toArray` to only get a certain amount
  247. var count = !hasRest(pattern) && pattern.elements.length;
  248. // so we need to ensure that the `arrayRef` is an array, `scope.toArray` will
  249. // return a locally bound identifier if it's been inferred to be an array,
  250. // otherwise it'll be a call to a helper that will ensure it's one
  251. var toArray = this.toArray(arrayRef, count);
  252. if (t.isIdentifier(toArray)) {
  253. // we've been given an identifier so it must have been inferred to be an
  254. // array
  255. arrayRef = toArray;
  256. } else {
  257. arrayRef = this.scope.generateUidIdentifierBasedOnNode(arrayRef);
  258. this.arrays[arrayRef.name] = true;
  259. this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray));
  260. }
  261. //
  262. for (var i = 0; i < pattern.elements.length; i++) {
  263. var elem = pattern.elements[i];
  264. // hole
  265. if (!elem) continue;
  266. var elemRef = /*istanbul ignore next*/void 0;
  267. if (t.isRestElement(elem)) {
  268. elemRef = this.toArray(arrayRef);
  269. if (i > 0) {
  270. elemRef = t.callExpression(t.memberExpression(elemRef, t.identifier("slice")), [t.numericLiteral(i)]);
  271. }
  272. // set the element to the rest element argument since we've dealt with it
  273. // being a rest already
  274. elem = elem.argument;
  275. } else {
  276. elemRef = t.memberExpression(arrayRef, t.numericLiteral(i), true);
  277. }
  278. this.push(elem, elemRef);
  279. }
  280. };
  281. DestructuringTransformer.prototype.init = function init(pattern, ref) {
  282. // trying to destructure a value that we can't evaluate more than once so we
  283. // need to save it to a variable
  284. if (!t.isArrayExpression(ref) && !t.isMemberExpression(ref)) {
  285. var memo = this.scope.maybeGenerateMemoised(ref, true);
  286. if (memo) {
  287. this.nodes.push(this.buildVariableDeclaration(memo, ref));
  288. ref = memo;
  289. }
  290. }
  291. //
  292. this.push(pattern, ref);
  293. return this.nodes;
  294. };
  295. return DestructuringTransformer;
  296. }();
  297. return {
  298. visitor: { /*istanbul ignore next*/
  299. ExportNamedDeclaration: function ExportNamedDeclaration(path) {
  300. var declaration = path.get("declaration");
  301. if (!declaration.isVariableDeclaration()) return;
  302. if (!variableDeclarationHasPattern(declaration.node)) return;
  303. var specifiers = [];
  304. for (var name in path.getOuterBindingIdentifiers(path)) {
  305. var id = t.identifier(name);
  306. specifiers.push(t.exportSpecifier(id, id));
  307. }
  308. // Split the declaration and export list into two declarations so that the variable
  309. // declaration can be split up later without needing to worry about not being a
  310. // top-level statement.
  311. path.replaceWith(declaration.node);
  312. path.insertAfter(t.exportNamedDeclaration(null, specifiers));
  313. },
  314. /*istanbul ignore next*/ForXStatement: function ForXStatement(path, file) {
  315. /*istanbul ignore next*/var node = path.node;
  316. /*istanbul ignore next*/var scope = path.scope;
  317. var left = node.left;
  318. if (t.isPattern(left)) {
  319. // for ({ length: k } in { abc: 3 });
  320. var temp = scope.generateUidIdentifier("ref");
  321. node.left = t.variableDeclaration("var", [t.variableDeclarator(temp)]);
  322. path.ensureBlock();
  323. node.body.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(left, temp)]));
  324. return;
  325. }
  326. if (!t.isVariableDeclaration(left)) return;
  327. var pattern = left.declarations[0].id;
  328. if (!t.isPattern(pattern)) return;
  329. var key = scope.generateUidIdentifier("ref");
  330. node.left = t.variableDeclaration(left.kind, [t.variableDeclarator(key, null)]);
  331. var nodes = [];
  332. var destructuring = new DestructuringTransformer({
  333. kind: left.kind,
  334. file: file,
  335. scope: scope,
  336. nodes: nodes
  337. });
  338. destructuring.init(pattern, key);
  339. path.ensureBlock();
  340. var block = node.body;
  341. block.body = nodes.concat(block.body);
  342. },
  343. /*istanbul ignore next*/CatchClause: function CatchClause(_ref6, file) {
  344. /*istanbul ignore next*/var node = _ref6.node;
  345. /*istanbul ignore next*/var scope = _ref6.scope;
  346. var pattern = node.param;
  347. if (!t.isPattern(pattern)) return;
  348. var ref = scope.generateUidIdentifier("ref");
  349. node.param = ref;
  350. var nodes = [];
  351. var destructuring = new DestructuringTransformer({
  352. kind: "let",
  353. file: file,
  354. scope: scope,
  355. nodes: nodes
  356. });
  357. destructuring.init(pattern, ref);
  358. node.body.body = nodes.concat(node.body.body);
  359. },
  360. /*istanbul ignore next*/AssignmentExpression: function AssignmentExpression(path, file) {
  361. /*istanbul ignore next*/var node = path.node;
  362. /*istanbul ignore next*/var scope = path.scope;
  363. if (!t.isPattern(node.left)) return;
  364. var nodes = [];
  365. var destructuring = new DestructuringTransformer({
  366. operator: node.operator,
  367. file: file,
  368. scope: scope,
  369. nodes: nodes
  370. });
  371. var ref = /*istanbul ignore next*/void 0;
  372. if (path.isCompletionRecord() || !path.parentPath.isExpressionStatement()) {
  373. ref = scope.generateUidIdentifierBasedOnNode(node.right, "ref");
  374. nodes.push(t.variableDeclaration("var", [t.variableDeclarator(ref, node.right)]));
  375. if (t.isArrayExpression(node.right)) {
  376. destructuring.arrays[ref.name] = true;
  377. }
  378. }
  379. destructuring.init(node.left, ref || node.right);
  380. if (ref) {
  381. nodes.push(t.expressionStatement(ref));
  382. }
  383. path.replaceWithMultiple(nodes);
  384. },
  385. /*istanbul ignore next*/VariableDeclaration: function VariableDeclaration(path, file) {
  386. /*istanbul ignore next*/var node = path.node;
  387. /*istanbul ignore next*/var scope = path.scope;
  388. /*istanbul ignore next*/var parent = path.parent;
  389. if (t.isForXStatement(parent)) return;
  390. if (!parent || !path.container) return; // i don't know why this is necessary - TODO
  391. if (!variableDeclarationHasPattern(node)) return;
  392. var nodes = [];
  393. var declar = /*istanbul ignore next*/void 0;
  394. for (var i = 0; i < node.declarations.length; i++) {
  395. declar = node.declarations[i];
  396. var patternId = declar.init;
  397. var pattern = declar.id;
  398. var destructuring = new DestructuringTransformer({
  399. blockHoist: node._blockHoist,
  400. nodes: nodes,
  401. scope: scope,
  402. kind: node.kind,
  403. file: file
  404. });
  405. if (t.isPattern(pattern)) {
  406. destructuring.init(pattern, patternId);
  407. if (+i !== node.declarations.length - 1) {
  408. // we aren't the last declarator so let's just make the
  409. // last transformed node inherit from us
  410. t.inherits(nodes[nodes.length - 1], declar);
  411. }
  412. } else {
  413. nodes.push(t.inherits(destructuring.buildVariableAssignment(declar.id, declar.init), declar));
  414. }
  415. }
  416. path.replaceWithMultiple(nodes);
  417. }
  418. }
  419. };
  420. };
  421. /*istanbul ignore next*/
  422. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  423. module.exports = exports["default"]; /* eslint max-len: 0 */