You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
17 KiB

5 years ago
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _helperPluginUtils = require("@babel/helper-plugin-utils");
  7. var _core = require("@babel/core");
  8. var _default = (0, _helperPluginUtils.declare)((api, options) => {
  9. api.assertVersion(7);
  10. const {
  11. loose = false,
  12. useBuiltIns = false
  13. } = options;
  14. if (typeof loose !== "boolean") {
  15. throw new Error(`.loose must be a boolean or undefined`);
  16. }
  17. const arrayOnlySpread = loose;
  18. function getExtendsHelper(file) {
  19. return useBuiltIns ? _core.types.memberExpression(_core.types.identifier("Object"), _core.types.identifier("assign")) : file.addHelper("extends");
  20. }
  21. function variableDeclarationHasPattern(node) {
  22. for (const declar of node.declarations) {
  23. if (_core.types.isPattern(declar.id)) {
  24. return true;
  25. }
  26. }
  27. return false;
  28. }
  29. function hasRest(pattern) {
  30. for (const elem of pattern.elements) {
  31. if (_core.types.isRestElement(elem)) {
  32. return true;
  33. }
  34. }
  35. return false;
  36. }
  37. function hasObjectRest(pattern) {
  38. for (const elem of pattern.properties) {
  39. if (_core.types.isRestElement(elem)) {
  40. return true;
  41. }
  42. }
  43. return false;
  44. }
  45. const STOP_TRAVERSAL = {};
  46. const arrayUnpackVisitor = (node, ancestors, state) => {
  47. if (!ancestors.length) {
  48. return;
  49. }
  50. if (_core.types.isIdentifier(node) && _core.types.isReferenced(node, ancestors[ancestors.length - 1]) && state.bindings[node.name]) {
  51. state.deopt = true;
  52. throw STOP_TRAVERSAL;
  53. }
  54. };
  55. class DestructuringTransformer {
  56. constructor(opts) {
  57. this.blockHoist = opts.blockHoist;
  58. this.operator = opts.operator;
  59. this.arrays = {};
  60. this.nodes = opts.nodes || [];
  61. this.scope = opts.scope;
  62. this.kind = opts.kind;
  63. this.arrayOnlySpread = opts.arrayOnlySpread;
  64. this.addHelper = opts.addHelper;
  65. }
  66. buildVariableAssignment(id, init) {
  67. let op = this.operator;
  68. if (_core.types.isMemberExpression(id)) op = "=";
  69. let node;
  70. if (op) {
  71. node = _core.types.expressionStatement(_core.types.assignmentExpression(op, id, _core.types.cloneNode(init) || this.scope.buildUndefinedNode()));
  72. } else {
  73. node = _core.types.variableDeclaration(this.kind, [_core.types.variableDeclarator(id, _core.types.cloneNode(init))]);
  74. }
  75. node._blockHoist = this.blockHoist;
  76. return node;
  77. }
  78. buildVariableDeclaration(id, init) {
  79. const declar = _core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.cloneNode(id), _core.types.cloneNode(init))]);
  80. declar._blockHoist = this.blockHoist;
  81. return declar;
  82. }
  83. push(id, _init) {
  84. const init = _core.types.cloneNode(_init);
  85. if (_core.types.isObjectPattern(id)) {
  86. this.pushObjectPattern(id, init);
  87. } else if (_core.types.isArrayPattern(id)) {
  88. this.pushArrayPattern(id, init);
  89. } else if (_core.types.isAssignmentPattern(id)) {
  90. this.pushAssignmentPattern(id, init);
  91. } else {
  92. this.nodes.push(this.buildVariableAssignment(id, init));
  93. }
  94. }
  95. toArray(node, count) {
  96. if (this.arrayOnlySpread || _core.types.isIdentifier(node) && this.arrays[node.name]) {
  97. return node;
  98. } else {
  99. return this.scope.toArray(node, count);
  100. }
  101. }
  102. pushAssignmentPattern({
  103. left,
  104. right
  105. }, valueRef) {
  106. const tempId = this.scope.generateUidIdentifierBasedOnNode(valueRef);
  107. this.nodes.push(this.buildVariableDeclaration(tempId, valueRef));
  108. const tempConditional = _core.types.conditionalExpression(_core.types.binaryExpression("===", _core.types.cloneNode(tempId), this.scope.buildUndefinedNode()), right, _core.types.cloneNode(tempId));
  109. if (_core.types.isPattern(left)) {
  110. let patternId;
  111. let node;
  112. if (this.kind === "const") {
  113. patternId = this.scope.generateUidIdentifier(tempId.name);
  114. node = this.buildVariableDeclaration(patternId, tempConditional);
  115. } else {
  116. patternId = tempId;
  117. node = _core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.cloneNode(tempId), tempConditional));
  118. }
  119. this.nodes.push(node);
  120. this.push(left, patternId);
  121. } else {
  122. this.nodes.push(this.buildVariableAssignment(left, tempConditional));
  123. }
  124. }
  125. pushObjectRest(pattern, objRef, spreadProp, spreadPropIndex) {
  126. const keys = [];
  127. let allLiteral = true;
  128. for (let i = 0; i < pattern.properties.length; i++) {
  129. const prop = pattern.properties[i];
  130. if (i >= spreadPropIndex) break;
  131. if (_core.types.isRestElement(prop)) continue;
  132. const key = prop.key;
  133. if (_core.types.isIdentifier(key) && !prop.computed) {
  134. keys.push(_core.types.stringLiteral(key.name));
  135. } else if (_core.types.isTemplateLiteral(prop.key)) {
  136. keys.push(_core.types.cloneNode(prop.key));
  137. } else if (_core.types.isLiteral(key)) {
  138. keys.push(_core.types.stringLiteral(String(key.value)));
  139. } else {
  140. keys.push(_core.types.cloneNode(key));
  141. allLiteral = false;
  142. }
  143. }
  144. let value;
  145. if (keys.length === 0) {
  146. value = _core.types.callExpression(getExtendsHelper(this), [_core.types.objectExpression([]), _core.types.cloneNode(objRef)]);
  147. } else {
  148. let keyExpression = _core.types.arrayExpression(keys);
  149. if (!allLiteral) {
  150. keyExpression = _core.types.callExpression(_core.types.memberExpression(keyExpression, _core.types.identifier("map")), [this.addHelper("toPropertyKey")]);
  151. }
  152. value = _core.types.callExpression(this.addHelper(`objectWithoutProperties${loose ? "Loose" : ""}`), [_core.types.cloneNode(objRef), keyExpression]);
  153. }
  154. this.nodes.push(this.buildVariableAssignment(spreadProp.argument, value));
  155. }
  156. pushObjectProperty(prop, propRef) {
  157. if (_core.types.isLiteral(prop.key)) prop.computed = true;
  158. const pattern = prop.value;
  159. const objRef = _core.types.memberExpression(_core.types.cloneNode(propRef), prop.key, prop.computed);
  160. if (_core.types.isPattern(pattern)) {
  161. this.push(pattern, objRef);
  162. } else {
  163. this.nodes.push(this.buildVariableAssignment(pattern, objRef));
  164. }
  165. }
  166. pushObjectPattern(pattern, objRef) {
  167. if (!pattern.properties.length) {
  168. this.nodes.push(_core.types.expressionStatement(_core.types.callExpression(this.addHelper("objectDestructuringEmpty"), [objRef])));
  169. }
  170. if (pattern.properties.length > 1 && !this.scope.isStatic(objRef)) {
  171. const temp = this.scope.generateUidIdentifierBasedOnNode(objRef);
  172. this.nodes.push(this.buildVariableDeclaration(temp, objRef));
  173. objRef = temp;
  174. }
  175. if (hasObjectRest(pattern)) {
  176. let copiedPattern;
  177. for (let i = 0; i < pattern.properties.length; i++) {
  178. const prop = pattern.properties[i];
  179. if (_core.types.isRestElement(prop)) {
  180. break;
  181. }
  182. const key = prop.key;
  183. if (prop.computed && !this.scope.isPure(key)) {
  184. const name = this.scope.generateUidIdentifierBasedOnNode(key);
  185. this.nodes.push(this.buildVariableDeclaration(name, key));
  186. if (!copiedPattern) {
  187. copiedPattern = pattern = Object.assign({}, pattern, {
  188. properties: pattern.properties.slice()
  189. });
  190. }
  191. copiedPattern.properties[i] = Object.assign({}, copiedPattern.properties[i], {
  192. key: name
  193. });
  194. }
  195. }
  196. }
  197. for (let i = 0; i < pattern.properties.length; i++) {
  198. const prop = pattern.properties[i];
  199. if (_core.types.isRestElement(prop)) {
  200. this.pushObjectRest(pattern, objRef, prop, i);
  201. } else {
  202. this.pushObjectProperty(prop, objRef);
  203. }
  204. }
  205. }
  206. canUnpackArrayPattern(pattern, arr) {
  207. if (!_core.types.isArrayExpression(arr)) return false;
  208. if (pattern.elements.length > arr.elements.length) return;
  209. if (pattern.elements.length < arr.elements.length && !hasRest(pattern)) {
  210. return false;
  211. }
  212. for (const elem of pattern.elements) {
  213. if (!elem) return false;
  214. if (_core.types.isMemberExpression(elem)) return false;
  215. }
  216. for (const elem of arr.elements) {
  217. if (_core.types.isSpreadElement(elem)) return false;
  218. if (_core.types.isCallExpression(elem)) return false;
  219. if (_core.types.isMemberExpression(elem)) return false;
  220. }
  221. const bindings = _core.types.getBindingIdentifiers(pattern);
  222. const state = {
  223. deopt: false,
  224. bindings
  225. };
  226. try {
  227. _core.types.traverse(arr, arrayUnpackVisitor, state);
  228. } catch (e) {
  229. if (e !== STOP_TRAVERSAL) throw e;
  230. }
  231. return !state.deopt;
  232. }
  233. pushUnpackedArrayPattern(pattern, arr) {
  234. for (let i = 0; i < pattern.elements.length; i++) {
  235. const elem = pattern.elements[i];
  236. if (_core.types.isRestElement(elem)) {
  237. this.push(elem.argument, _core.types.arrayExpression(arr.elements.slice(i)));
  238. } else {
  239. this.push(elem, arr.elements[i]);
  240. }
  241. }
  242. }
  243. pushArrayPattern(pattern, arrayRef) {
  244. if (!pattern.elements) return;
  245. if (this.canUnpackArrayPattern(pattern, arrayRef)) {
  246. return this.pushUnpackedArrayPattern(pattern, arrayRef);
  247. }
  248. const count = !hasRest(pattern) && pattern.elements.length;
  249. const toArray = this.toArray(arrayRef, count);
  250. if (_core.types.isIdentifier(toArray)) {
  251. arrayRef = toArray;
  252. } else {
  253. arrayRef = this.scope.generateUidIdentifierBasedOnNode(arrayRef);
  254. this.arrays[arrayRef.name] = true;
  255. this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray));
  256. }
  257. for (let i = 0; i < pattern.elements.length; i++) {
  258. let elem = pattern.elements[i];
  259. if (!elem) continue;
  260. let elemRef;
  261. if (_core.types.isRestElement(elem)) {
  262. elemRef = this.toArray(arrayRef);
  263. elemRef = _core.types.callExpression(_core.types.memberExpression(elemRef, _core.types.identifier("slice")), [_core.types.numericLiteral(i)]);
  264. elem = elem.argument;
  265. } else {
  266. elemRef = _core.types.memberExpression(arrayRef, _core.types.numericLiteral(i), true);
  267. }
  268. this.push(elem, elemRef);
  269. }
  270. }
  271. init(pattern, ref) {
  272. if (!_core.types.isArrayExpression(ref) && !_core.types.isMemberExpression(ref)) {
  273. const memo = this.scope.maybeGenerateMemoised(ref, true);
  274. if (memo) {
  275. this.nodes.push(this.buildVariableDeclaration(memo, _core.types.cloneNode(ref)));
  276. ref = memo;
  277. }
  278. }
  279. this.push(pattern, ref);
  280. return this.nodes;
  281. }
  282. }
  283. return {
  284. name: "transform-destructuring",
  285. visitor: {
  286. ExportNamedDeclaration(path) {
  287. const declaration = path.get("declaration");
  288. if (!declaration.isVariableDeclaration()) return;
  289. if (!variableDeclarationHasPattern(declaration.node)) return;
  290. const specifiers = [];
  291. for (const name of Object.keys(path.getOuterBindingIdentifiers(path))) {
  292. specifiers.push(_core.types.exportSpecifier(_core.types.identifier(name), _core.types.identifier(name)));
  293. }
  294. path.replaceWith(declaration.node);
  295. path.insertAfter(_core.types.exportNamedDeclaration(null, specifiers));
  296. },
  297. ForXStatement(path) {
  298. const {
  299. node,
  300. scope
  301. } = path;
  302. const left = node.left;
  303. if (_core.types.isPattern(left)) {
  304. const temp = scope.generateUidIdentifier("ref");
  305. node.left = _core.types.variableDeclaration("var", [_core.types.variableDeclarator(temp)]);
  306. path.ensureBlock();
  307. if (node.body.body.length === 0 && path.isCompletionRecord()) {
  308. node.body.body.unshift(_core.types.expressionStatement(scope.buildUndefinedNode()));
  309. }
  310. node.body.body.unshift(_core.types.expressionStatement(_core.types.assignmentExpression("=", left, temp)));
  311. return;
  312. }
  313. if (!_core.types.isVariableDeclaration(left)) return;
  314. const pattern = left.declarations[0].id;
  315. if (!_core.types.isPattern(pattern)) return;
  316. const key = scope.generateUidIdentifier("ref");
  317. node.left = _core.types.variableDeclaration(left.kind, [_core.types.variableDeclarator(key, null)]);
  318. const nodes = [];
  319. const destructuring = new DestructuringTransformer({
  320. kind: left.kind,
  321. scope: scope,
  322. nodes: nodes,
  323. arrayOnlySpread,
  324. addHelper: name => this.addHelper(name)
  325. });
  326. destructuring.init(pattern, key);
  327. path.ensureBlock();
  328. const block = node.body;
  329. block.body = nodes.concat(block.body);
  330. },
  331. CatchClause({
  332. node,
  333. scope
  334. }) {
  335. const pattern = node.param;
  336. if (!_core.types.isPattern(pattern)) return;
  337. const ref = scope.generateUidIdentifier("ref");
  338. node.param = ref;
  339. const nodes = [];
  340. const destructuring = new DestructuringTransformer({
  341. kind: "let",
  342. scope: scope,
  343. nodes: nodes,
  344. arrayOnlySpread,
  345. addHelper: name => this.addHelper(name)
  346. });
  347. destructuring.init(pattern, ref);
  348. node.body.body = nodes.concat(node.body.body);
  349. },
  350. AssignmentExpression(path) {
  351. const {
  352. node,
  353. scope
  354. } = path;
  355. if (!_core.types.isPattern(node.left)) return;
  356. const nodes = [];
  357. const destructuring = new DestructuringTransformer({
  358. operator: node.operator,
  359. scope: scope,
  360. nodes: nodes,
  361. arrayOnlySpread,
  362. addHelper: name => this.addHelper(name)
  363. });
  364. let ref;
  365. if (path.isCompletionRecord() || !path.parentPath.isExpressionStatement()) {
  366. ref = scope.generateUidIdentifierBasedOnNode(node.right, "ref");
  367. nodes.push(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(ref, node.right)]));
  368. if (_core.types.isArrayExpression(node.right)) {
  369. destructuring.arrays[ref.name] = true;
  370. }
  371. }
  372. destructuring.init(node.left, ref || node.right);
  373. if (ref) {
  374. if (path.parentPath.isArrowFunctionExpression()) {
  375. path.replaceWith(_core.types.blockStatement([]));
  376. nodes.push(_core.types.returnStatement(_core.types.cloneNode(ref)));
  377. } else {
  378. nodes.push(_core.types.expressionStatement(_core.types.cloneNode(ref)));
  379. }
  380. }
  381. path.replaceWithMultiple(nodes);
  382. },
  383. VariableDeclaration(path) {
  384. const {
  385. node,
  386. scope,
  387. parent
  388. } = path;
  389. if (_core.types.isForXStatement(parent)) return;
  390. if (!parent || !path.container) return;
  391. if (!variableDeclarationHasPattern(node)) return;
  392. const nodeKind = node.kind;
  393. const nodes = [];
  394. let declar;
  395. for (let i = 0; i < node.declarations.length; i++) {
  396. declar = node.declarations[i];
  397. const patternId = declar.init;
  398. const pattern = declar.id;
  399. const destructuring = new DestructuringTransformer({
  400. blockHoist: node._blockHoist,
  401. nodes: nodes,
  402. scope: scope,
  403. kind: node.kind,
  404. arrayOnlySpread,
  405. addHelper: name => this.addHelper(name)
  406. });
  407. if (_core.types.isPattern(pattern)) {
  408. destructuring.init(pattern, patternId);
  409. if (+i !== node.declarations.length - 1) {
  410. _core.types.inherits(nodes[nodes.length - 1], declar);
  411. }
  412. } else {
  413. nodes.push(_core.types.inherits(destructuring.buildVariableAssignment(declar.id, _core.types.cloneNode(declar.init)), declar));
  414. }
  415. }
  416. let tail = null;
  417. const nodesOut = [];
  418. for (const node of nodes) {
  419. if (tail !== null && _core.types.isVariableDeclaration(node)) {
  420. tail.declarations.push(...node.declarations);
  421. } else {
  422. node.kind = nodeKind;
  423. nodesOut.push(node);
  424. tail = _core.types.isVariableDeclaration(node) ? node : null;
  425. }
  426. }
  427. for (const nodeOut of nodesOut) {
  428. if (!nodeOut.declarations) continue;
  429. for (const declaration of nodeOut.declarations) {
  430. const {
  431. name
  432. } = declaration.id;
  433. if (scope.bindings[name]) {
  434. scope.bindings[name].kind = nodeOut.kind;
  435. }
  436. }
  437. }
  438. if (nodesOut.length === 1) {
  439. path.replaceWith(nodesOut[0]);
  440. } else {
  441. path.replaceWithMultiple(nodesOut);
  442. }
  443. }
  444. }
  445. };
  446. });
  447. exports.default = _default;