Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

682 строки
20 KiB

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.buildPrivateNamesMap = buildPrivateNamesMap;
  6. exports.buildPrivateNamesNodes = buildPrivateNamesNodes;
  7. exports.transformPrivateNamesUsage = transformPrivateNamesUsage;
  8. exports.buildFieldsInitNodes = buildFieldsInitNodes;
  9. var _core = require("@babel/core");
  10. var _helperReplaceSupers = _interopRequireWildcard(require("@babel/helper-replace-supers"));
  11. var _helperMemberExpressionToFunctions = _interopRequireDefault(require("@babel/helper-member-expression-to-functions"));
  12. var _helperOptimiseCallExpression = _interopRequireDefault(require("@babel/helper-optimise-call-expression"));
  13. var ts = _interopRequireWildcard(require("./typescript"));
  14. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  15. function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
  16. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  17. function buildPrivateNamesMap(props) {
  18. const privateNamesMap = new Map();
  19. for (const prop of props) {
  20. const isPrivate = prop.isPrivate();
  21. const isMethod = !prop.isProperty();
  22. const isInstance = !prop.node.static;
  23. if (isPrivate) {
  24. const {
  25. name
  26. } = prop.node.key.id;
  27. const update = privateNamesMap.has(name) ? privateNamesMap.get(name) : {
  28. id: prop.scope.generateUidIdentifier(name),
  29. static: !isInstance,
  30. method: isMethod
  31. };
  32. if (prop.node.kind === "get") {
  33. update.getId = prop.scope.generateUidIdentifier(`get_${name}`);
  34. } else if (prop.node.kind === "set") {
  35. update.setId = prop.scope.generateUidIdentifier(`set_${name}`);
  36. } else if (prop.node.kind === "method") {
  37. update.methodId = prop.scope.generateUidIdentifier(name);
  38. }
  39. privateNamesMap.set(name, update);
  40. }
  41. }
  42. return privateNamesMap;
  43. }
  44. function buildPrivateNamesNodes(privateNamesMap, loose, state) {
  45. const initNodes = [];
  46. for (const [name, value] of privateNamesMap) {
  47. const {
  48. id,
  49. static: isStatic,
  50. method: isMethod,
  51. getId,
  52. setId
  53. } = value;
  54. const isAccessor = getId || setId;
  55. if (loose) {
  56. initNodes.push(_core.template.statement.ast`
  57. var ${id} = ${state.addHelper("classPrivateFieldLooseKey")}("${name}")
  58. `);
  59. } else if (isMethod && !isStatic) {
  60. if (isAccessor) {
  61. initNodes.push(_core.template.statement.ast`var ${id} = new WeakMap();`);
  62. } else {
  63. initNodes.push(_core.template.statement.ast`var ${id} = new WeakSet();`);
  64. }
  65. } else if (!isStatic) {
  66. initNodes.push(_core.template.statement.ast`var ${id} = new WeakMap();`);
  67. }
  68. }
  69. return initNodes;
  70. }
  71. const privateNameVisitor = {
  72. PrivateName(path) {
  73. const {
  74. privateNamesMap
  75. } = this;
  76. const {
  77. node,
  78. parentPath
  79. } = path;
  80. if (!parentPath.isMemberExpression({
  81. property: node
  82. })) return;
  83. if (!privateNamesMap.has(node.id.name)) return;
  84. this.handle(parentPath);
  85. },
  86. Class(path) {
  87. const {
  88. privateNamesMap
  89. } = this;
  90. const body = path.get("body.body");
  91. const visiblePrivateNames = new Map(privateNamesMap);
  92. const redeclared = [];
  93. for (const prop of body) {
  94. if (!prop.isPrivate()) continue;
  95. const {
  96. name
  97. } = prop.node.key.id;
  98. visiblePrivateNames.delete(name);
  99. redeclared.push(name);
  100. }
  101. if (!redeclared.length) {
  102. return;
  103. }
  104. path.get("body").traverse(privateNameNestedVisitor, Object.assign({}, this, {
  105. redeclared
  106. }));
  107. path.traverse(privateNameVisitor, Object.assign({}, this, {
  108. privateNamesMap: visiblePrivateNames
  109. }));
  110. path.skipKey("body");
  111. }
  112. };
  113. const privateNameNestedVisitor = _core.traverse.visitors.merge([{
  114. PrivateName(path) {
  115. const {
  116. redeclared
  117. } = this;
  118. const {
  119. name
  120. } = path.node.id;
  121. if (redeclared.includes(name)) path.skip();
  122. }
  123. }, {
  124. PrivateName: privateNameVisitor.PrivateName
  125. }, _helperReplaceSupers.environmentVisitor]);
  126. const privateNameHandlerSpec = {
  127. memoise(member, count) {
  128. const {
  129. scope
  130. } = member;
  131. const {
  132. object
  133. } = member.node;
  134. const memo = scope.maybeGenerateMemoised(object);
  135. if (!memo) {
  136. return;
  137. }
  138. this.memoiser.set(object, memo, count);
  139. },
  140. receiver(member) {
  141. const {
  142. object
  143. } = member.node;
  144. if (this.memoiser.has(object)) {
  145. return _core.types.cloneNode(this.memoiser.get(object));
  146. }
  147. return _core.types.cloneNode(object);
  148. },
  149. get(member) {
  150. const {
  151. classRef,
  152. privateNamesMap,
  153. file
  154. } = this;
  155. const {
  156. name
  157. } = member.node.property.id;
  158. const {
  159. id,
  160. static: isStatic,
  161. method: isMethod,
  162. methodId,
  163. getId,
  164. setId
  165. } = privateNamesMap.get(name);
  166. const isAccessor = getId || setId;
  167. if (isStatic) {
  168. const helperName = isMethod && !isAccessor ? "classStaticPrivateMethodGet" : "classStaticPrivateFieldSpecGet";
  169. return _core.types.callExpression(file.addHelper(helperName), [this.receiver(member), _core.types.cloneNode(classRef), _core.types.cloneNode(id)]);
  170. }
  171. if (isMethod) {
  172. if (isAccessor) {
  173. return _core.types.callExpression(file.addHelper("classPrivateFieldGet"), [this.receiver(member), _core.types.cloneNode(id)]);
  174. }
  175. return _core.types.callExpression(file.addHelper("classPrivateMethodGet"), [this.receiver(member), _core.types.cloneNode(id), _core.types.cloneNode(methodId)]);
  176. }
  177. return _core.types.callExpression(file.addHelper("classPrivateFieldGet"), [this.receiver(member), _core.types.cloneNode(id)]);
  178. },
  179. set(member, value) {
  180. const {
  181. classRef,
  182. privateNamesMap,
  183. file
  184. } = this;
  185. const {
  186. name
  187. } = member.node.property.id;
  188. const {
  189. id,
  190. static: isStatic,
  191. method: isMethod,
  192. setId,
  193. getId
  194. } = privateNamesMap.get(name);
  195. const isAccessor = getId || setId;
  196. if (isStatic) {
  197. const helperName = isMethod && !isAccessor ? "classStaticPrivateMethodSet" : "classStaticPrivateFieldSpecSet";
  198. return _core.types.callExpression(file.addHelper(helperName), [this.receiver(member), _core.types.cloneNode(classRef), _core.types.cloneNode(id), value]);
  199. }
  200. if (isMethod) {
  201. if (setId) {
  202. return _core.types.callExpression(file.addHelper("classPrivateFieldSet"), [this.receiver(member), _core.types.cloneNode(id), value]);
  203. }
  204. return _core.types.callExpression(file.addHelper("classPrivateMethodSet"), []);
  205. }
  206. return _core.types.callExpression(file.addHelper("classPrivateFieldSet"), [this.receiver(member), _core.types.cloneNode(id), value]);
  207. },
  208. destructureSet(member) {
  209. const {
  210. privateNamesMap,
  211. file
  212. } = this;
  213. const {
  214. name
  215. } = member.node.property.id;
  216. const {
  217. id
  218. } = privateNamesMap.get(name);
  219. return _core.types.memberExpression(_core.types.callExpression(file.addHelper("classPrivateFieldDestructureSet"), [this.receiver(member), _core.types.cloneNode(id)]), _core.types.identifier("value"));
  220. },
  221. call(member, args) {
  222. this.memoise(member, 1);
  223. return (0, _helperOptimiseCallExpression.default)(this.get(member), this.receiver(member), args);
  224. }
  225. };
  226. const privateNameHandlerLoose = {
  227. handle(member) {
  228. const {
  229. privateNamesMap,
  230. file
  231. } = this;
  232. const {
  233. object
  234. } = member.node;
  235. const {
  236. name
  237. } = member.node.property.id;
  238. member.replaceWith(_core.template.expression`BASE(REF, PROP)[PROP]`({
  239. BASE: file.addHelper("classPrivateFieldLooseBase"),
  240. REF: object,
  241. PROP: privateNamesMap.get(name).id
  242. }));
  243. }
  244. };
  245. function transformPrivateNamesUsage(ref, path, privateNamesMap, loose, state) {
  246. if (!privateNamesMap.size) return;
  247. const body = path.get("body");
  248. if (loose) {
  249. body.traverse(privateNameVisitor, Object.assign({
  250. privateNamesMap,
  251. file: state
  252. }, privateNameHandlerLoose));
  253. } else {
  254. (0, _helperMemberExpressionToFunctions.default)(body, privateNameVisitor, Object.assign({
  255. privateNamesMap,
  256. classRef: ref,
  257. file: state
  258. }, privateNameHandlerSpec));
  259. }
  260. }
  261. function buildPrivateFieldInitLoose(ref, prop, privateNamesMap) {
  262. const {
  263. id
  264. } = privateNamesMap.get(prop.node.key.id.name);
  265. const value = prop.node.value || prop.scope.buildUndefinedNode();
  266. return _core.template.statement.ast`
  267. Object.defineProperty(${ref}, ${id}, {
  268. // configurable is false by default
  269. // enumerable is false by default
  270. writable: true,
  271. value: ${value}
  272. });
  273. `;
  274. }
  275. function buildPrivateInstanceFieldInitSpec(ref, prop, privateNamesMap) {
  276. const {
  277. id
  278. } = privateNamesMap.get(prop.node.key.id.name);
  279. const value = prop.node.value || prop.scope.buildUndefinedNode();
  280. return _core.template.statement.ast`${id}.set(${ref}, {
  281. // configurable is always false for private elements
  282. // enumerable is always false for private elements
  283. writable: true,
  284. value: ${value},
  285. })`;
  286. }
  287. function buildPrivateStaticFieldInitSpec(prop, privateNamesMap) {
  288. const privateName = privateNamesMap.get(prop.node.key.id.name);
  289. const {
  290. id,
  291. getId,
  292. setId,
  293. initAdded
  294. } = privateName;
  295. const isAccessor = getId || setId;
  296. if (!prop.isProperty() && (initAdded || !isAccessor)) return;
  297. if (isAccessor) {
  298. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  299. initAdded: true
  300. }));
  301. return _core.template.statement.ast`
  302. var ${id.name} = {
  303. // configurable is false by default
  304. // enumerable is false by default
  305. // writable is false by default
  306. get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
  307. set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
  308. }
  309. `;
  310. }
  311. const value = prop.node.value || prop.scope.buildUndefinedNode();
  312. return _core.template.statement.ast`
  313. var ${id} = {
  314. // configurable is false by default
  315. // enumerable is false by default
  316. writable: true,
  317. value: ${value}
  318. };
  319. `;
  320. }
  321. function buildPrivateMethodInitLoose(ref, prop, privateNamesMap) {
  322. const privateName = privateNamesMap.get(prop.node.key.id.name);
  323. const {
  324. methodId,
  325. id,
  326. getId,
  327. setId,
  328. initAdded
  329. } = privateName;
  330. if (initAdded) return;
  331. if (methodId) {
  332. return _core.template.statement.ast`
  333. Object.defineProperty(${ref}, ${id}, {
  334. // configurable is false by default
  335. // enumerable is false by default
  336. // writable is false by default
  337. value: ${methodId.name}
  338. });
  339. `;
  340. }
  341. const isAccessor = getId || setId;
  342. if (isAccessor) {
  343. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  344. initAdded: true
  345. }));
  346. return _core.template.statement.ast`
  347. Object.defineProperty(${ref}, ${id}, {
  348. // configurable is false by default
  349. // enumerable is false by default
  350. // writable is false by default
  351. get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
  352. set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
  353. });
  354. `;
  355. }
  356. }
  357. function buildPrivateInstanceMethodInitSpec(ref, prop, privateNamesMap) {
  358. const privateName = privateNamesMap.get(prop.node.key.id.name);
  359. const {
  360. id,
  361. getId,
  362. setId,
  363. initAdded
  364. } = privateName;
  365. if (initAdded) return;
  366. const isAccessor = getId || setId;
  367. if (isAccessor) {
  368. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  369. initAdded: true
  370. }));
  371. return _core.template.statement.ast`
  372. ${id}.set(${ref}, {
  373. get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
  374. set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
  375. });
  376. `;
  377. }
  378. return _core.template.statement.ast`${id}.add(${ref})`;
  379. }
  380. function buildPublicFieldInitLoose(ref, prop) {
  381. const {
  382. key,
  383. computed
  384. } = prop.node;
  385. const value = prop.node.value || prop.scope.buildUndefinedNode();
  386. return _core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.memberExpression(ref, key, computed || _core.types.isLiteral(key)), value));
  387. }
  388. function buildPublicFieldInitSpec(ref, prop, state) {
  389. const {
  390. key,
  391. computed
  392. } = prop.node;
  393. const value = prop.node.value || prop.scope.buildUndefinedNode();
  394. return _core.types.expressionStatement(_core.types.callExpression(state.addHelper("defineProperty"), [ref, computed || _core.types.isLiteral(key) ? key : _core.types.stringLiteral(key.name), value]));
  395. }
  396. function buildPrivateStaticMethodInitLoose(ref, prop, state, privateNamesMap) {
  397. const privateName = privateNamesMap.get(prop.node.key.id.name);
  398. const {
  399. id,
  400. methodId,
  401. getId,
  402. setId,
  403. initAdded
  404. } = privateName;
  405. if (initAdded) return;
  406. const isAccessor = getId || setId;
  407. if (isAccessor) {
  408. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  409. initAdded: true
  410. }));
  411. return _core.template.statement.ast`
  412. Object.defineProperty(${ref}, ${id}, {
  413. // configurable is false by default
  414. // enumerable is false by default
  415. // writable is false by default
  416. get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
  417. set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
  418. })
  419. `;
  420. }
  421. return _core.template.statement.ast`
  422. Object.defineProperty(${ref}, ${id}, {
  423. // configurable is false by default
  424. // enumerable is false by default
  425. // writable is false by default
  426. value: ${methodId.name}
  427. });
  428. `;
  429. }
  430. function buildPrivateMethodDeclaration(prop, privateNamesMap, loose = false) {
  431. const privateName = privateNamesMap.get(prop.node.key.id.name);
  432. const {
  433. id,
  434. methodId,
  435. getId,
  436. setId,
  437. getterDeclared,
  438. setterDeclared,
  439. static: isStatic
  440. } = privateName;
  441. const {
  442. params,
  443. body,
  444. generator,
  445. async
  446. } = prop.node;
  447. const methodValue = _core.types.functionExpression(methodId, params, body, generator, async);
  448. const isGetter = getId && !getterDeclared && params.length === 0;
  449. const isSetter = setId && !setterDeclared && params.length > 0;
  450. if (isGetter) {
  451. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  452. getterDeclared: true
  453. }));
  454. return _core.types.variableDeclaration("var", [_core.types.variableDeclarator(getId, methodValue)]);
  455. }
  456. if (isSetter) {
  457. privateNamesMap.set(prop.node.key.id.name, Object.assign({}, privateName, {
  458. setterDeclared: true
  459. }));
  460. return _core.types.variableDeclaration("var", [_core.types.variableDeclarator(setId, methodValue)]);
  461. }
  462. if (isStatic && !loose) {
  463. return _core.types.variableDeclaration("var", [_core.types.variableDeclarator(id, _core.types.functionExpression(id, params, body, generator, async))]);
  464. }
  465. return _core.types.variableDeclaration("var", [_core.types.variableDeclarator(methodId, methodValue)]);
  466. }
  467. const thisContextVisitor = _core.traverse.visitors.merge([{
  468. ThisExpression(path, state) {
  469. state.needsClassRef = true;
  470. path.replaceWith(_core.types.cloneNode(state.classRef));
  471. }
  472. }, _helperReplaceSupers.environmentVisitor]);
  473. function replaceThisContext(path, ref, superRef, file, loose) {
  474. const state = {
  475. classRef: ref,
  476. needsClassRef: false
  477. };
  478. const replacer = new _helperReplaceSupers.default({
  479. methodPath: path,
  480. isLoose: loose,
  481. superRef,
  482. file,
  483. getObjectRef() {
  484. state.needsClassRef = true;
  485. return path.node.static ? ref : _core.types.memberExpression(ref, _core.types.identifier("prototype"));
  486. }
  487. });
  488. replacer.replace();
  489. if (path.isProperty()) {
  490. path.traverse(thisContextVisitor, state);
  491. }
  492. return state.needsClassRef;
  493. }
  494. function buildFieldsInitNodes(ref, superRef, props, privateNamesMap, state, loose) {
  495. const staticNodes = [];
  496. const instanceNodes = [];
  497. let needsClassRef = false;
  498. for (const prop of props) {
  499. ts.assertFieldTransformed(prop);
  500. const isStatic = prop.node.static;
  501. const isInstance = !isStatic;
  502. const isPrivate = prop.isPrivate();
  503. const isPublic = !isPrivate;
  504. const isField = prop.isProperty();
  505. const isMethod = !isField;
  506. if (isStatic || isMethod && isPrivate) {
  507. const replaced = replaceThisContext(prop, ref, superRef, state, loose);
  508. needsClassRef = needsClassRef || replaced;
  509. }
  510. switch (true) {
  511. case isStatic && isPrivate && isField && loose:
  512. needsClassRef = true;
  513. staticNodes.push(buildPrivateFieldInitLoose(_core.types.cloneNode(ref), prop, privateNamesMap));
  514. break;
  515. case isStatic && isPrivate && isField && !loose:
  516. needsClassRef = true;
  517. staticNodes.push(buildPrivateStaticFieldInitSpec(prop, privateNamesMap));
  518. break;
  519. case isStatic && isPublic && isField && loose:
  520. needsClassRef = true;
  521. staticNodes.push(buildPublicFieldInitLoose(_core.types.cloneNode(ref), prop));
  522. break;
  523. case isStatic && isPublic && isField && !loose:
  524. needsClassRef = true;
  525. staticNodes.push(buildPublicFieldInitSpec(_core.types.cloneNode(ref), prop, state));
  526. break;
  527. case isInstance && isPrivate && isField && loose:
  528. instanceNodes.push(buildPrivateFieldInitLoose(_core.types.thisExpression(), prop, privateNamesMap));
  529. break;
  530. case isInstance && isPrivate && isField && !loose:
  531. instanceNodes.push(buildPrivateInstanceFieldInitSpec(_core.types.thisExpression(), prop, privateNamesMap));
  532. break;
  533. case isInstance && isPrivate && isMethod && loose:
  534. instanceNodes.unshift(buildPrivateMethodInitLoose(_core.types.thisExpression(), prop, privateNamesMap));
  535. staticNodes.push(buildPrivateMethodDeclaration(prop, privateNamesMap, loose));
  536. break;
  537. case isInstance && isPrivate && isMethod && !loose:
  538. instanceNodes.unshift(buildPrivateInstanceMethodInitSpec(_core.types.thisExpression(), prop, privateNamesMap));
  539. staticNodes.push(buildPrivateMethodDeclaration(prop, privateNamesMap, loose));
  540. break;
  541. case isStatic && isPrivate && isMethod && !loose:
  542. needsClassRef = true;
  543. staticNodes.push(buildPrivateStaticFieldInitSpec(prop, privateNamesMap));
  544. staticNodes.unshift(buildPrivateMethodDeclaration(prop, privateNamesMap, loose));
  545. break;
  546. case isStatic && isPrivate && isMethod && loose:
  547. needsClassRef = true;
  548. staticNodes.push(buildPrivateStaticMethodInitLoose(_core.types.cloneNode(ref), prop, state, privateNamesMap));
  549. staticNodes.unshift(buildPrivateMethodDeclaration(prop, privateNamesMap, loose));
  550. break;
  551. case isInstance && isPublic && isField && loose:
  552. instanceNodes.push(buildPublicFieldInitLoose(_core.types.thisExpression(), prop));
  553. break;
  554. case isInstance && isPublic && isField && !loose:
  555. instanceNodes.push(buildPublicFieldInitSpec(_core.types.thisExpression(), prop, state));
  556. break;
  557. default:
  558. throw new Error("Unreachable.");
  559. }
  560. }
  561. return {
  562. staticNodes: staticNodes.filter(Boolean),
  563. instanceNodes: instanceNodes.filter(Boolean),
  564. wrapClass(path) {
  565. for (const prop of props) {
  566. prop.remove();
  567. }
  568. if (!needsClassRef) return path;
  569. if (path.isClassExpression()) {
  570. path.scope.push({
  571. id: ref
  572. });
  573. path.replaceWith(_core.types.assignmentExpression("=", _core.types.cloneNode(ref), path.node));
  574. } else if (!path.node.id) {
  575. path.node.id = ref;
  576. }
  577. return path;
  578. }
  579. };
  580. }