index.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var core = require('@babel/core');
  4. var helperPluginUtils = require('@babel/helper-plugin-utils');
  5. function isNameOrLength(key) {
  6. if (core.types.isIdentifier(key)) {
  7. return key.name === "name" || key.name === "length";
  8. }
  9. if (core.types.isStringLiteral(key)) {
  10. return key.value === "name" || key.value === "length";
  11. }
  12. return false;
  13. }
  14. function isStaticFieldWithValue(node) {
  15. return (core.types.isClassProperty(node) || core.types.isClassPrivateProperty(node)) && node.static && !!node.value;
  16. }
  17. const hasReferenceVisitor = {
  18. ReferencedIdentifier(path, state) {
  19. if (path.node.name === state.name) {
  20. state.ref();
  21. path.stop();
  22. }
  23. },
  24. Scope(path, {
  25. name
  26. }) {
  27. if (path.scope.hasOwnBinding(name)) {
  28. path.skip();
  29. }
  30. }
  31. };
  32. function isReferenceOrThis(node, name) {
  33. return core.types.isThisExpression(node) || name && core.types.isIdentifier(node, {
  34. name
  35. });
  36. }
  37. const hasReferenceOrThisVisitor = {
  38. "ThisExpression|ReferencedIdentifier"(path, state) {
  39. if (isReferenceOrThis(path.node, state.name)) {
  40. state.ref();
  41. path.stop();
  42. }
  43. },
  44. FunctionParent(path, state) {
  45. if (path.isArrowFunctionExpression()) return;
  46. if (state.name && !path.scope.hasOwnBinding(state.name)) {
  47. path.traverse(hasReferenceVisitor, state);
  48. }
  49. path.skip();
  50. if (path.isMethod()) {
  51. if (path.requeueComputedKeyAndDecorators) {
  52. path.requeueComputedKeyAndDecorators();
  53. } else {
  54. require("@babel/traverse").NodePath.prototype.requeueComputedKeyAndDecorators.call(path);
  55. }
  56. }
  57. }
  58. };
  59. function getPotentiallyBuggyFieldsIndexes(path) {
  60. var _path$node$id;
  61. const buggyPublicStaticFieldsIndexes = [];
  62. let classReferenced = false;
  63. const className = (_path$node$id = path.node.id) == null ? void 0 : _path$node$id.name;
  64. const hasReferenceState = {
  65. name: className,
  66. ref: () => classReferenced = true
  67. };
  68. if (className) {
  69. for (const el of path.get("body.body")) {
  70. if (el.node.computed) {
  71. el.get("key").traverse(hasReferenceVisitor, hasReferenceState);
  72. if (classReferenced) break;
  73. }
  74. }
  75. }
  76. let nextPotentiallyBuggy = false;
  77. const {
  78. body
  79. } = path.node.body;
  80. for (let i = 0; i < body.length; i++) {
  81. const node = body[i];
  82. if (!nextPotentiallyBuggy) {
  83. if (core.types.isStaticBlock(node)) {
  84. classReferenced = true;
  85. nextPotentiallyBuggy = true;
  86. } else if (isStaticFieldWithValue(node)) {
  87. if (!classReferenced) {
  88. if (isReferenceOrThis(node.value, className)) {
  89. classReferenced = true;
  90. } else {
  91. path.get(`body.body.${i}.value`).traverse(hasReferenceOrThisVisitor, hasReferenceState);
  92. }
  93. }
  94. if (classReferenced) {
  95. nextPotentiallyBuggy = !path.scope.isPure(node.value);
  96. }
  97. }
  98. }
  99. if (core.types.isClassProperty(node, {
  100. static: true
  101. }) && (nextPotentiallyBuggy || node.computed || isNameOrLength(node.key))) {
  102. buggyPublicStaticFieldsIndexes.push(i);
  103. }
  104. }
  105. return buggyPublicStaticFieldsIndexes;
  106. }
  107. function getNameOrLengthStaticFieldsIndexes(path) {
  108. const indexes = [];
  109. const {
  110. body
  111. } = path.node.body;
  112. for (let i = 0; i < body.length; i++) {
  113. const node = body[i];
  114. if (core.types.isClassProperty(node, {
  115. static: true,
  116. computed: false
  117. }) && isNameOrLength(node.key)) {
  118. indexes.push(i);
  119. }
  120. }
  121. return indexes;
  122. }
  123. function toRanges(nums) {
  124. const ranges = [];
  125. if (nums.length === 0) return ranges;
  126. let start = nums[0];
  127. let end = start + 1;
  128. for (let i = 1; i < nums.length; i++) {
  129. if (nums[i] <= nums[i - 1]) {
  130. throw new Error("Internal Babel error: nums must be in ascending order");
  131. }
  132. if (nums[i] === end) {
  133. end++;
  134. } else {
  135. ranges.push([start, end]);
  136. start = nums[i];
  137. end = start + 1;
  138. }
  139. }
  140. ranges.push([start, end]);
  141. return ranges;
  142. }
  143. function buildFieldsReplacement(fields, scope, file) {
  144. return core.types.staticBlock(fields.map(field => {
  145. const key = field.computed || !core.types.isIdentifier(field.key) ? field.key : core.types.stringLiteral(field.key.name);
  146. return core.types.expressionStatement(core.types.callExpression(file.addHelper("defineProperty"), [core.types.thisExpression(), key, field.value || scope.buildUndefinedNode()]));
  147. }));
  148. }
  149. var index = helperPluginUtils.declare(api => {
  150. api.assertVersion("^7.0.0-0 || >8.0.0-alpha <8.0.0-beta");
  151. const setPublicClassFields = api.assumption("setPublicClassFields");
  152. return {
  153. name: "bugfix-v8-static-class-fields-redefine-readonly",
  154. visitor: {
  155. Class(path) {
  156. const ranges = toRanges(setPublicClassFields ? getNameOrLengthStaticFieldsIndexes(path) : getPotentiallyBuggyFieldsIndexes(path));
  157. for (let i = ranges.length - 1; i >= 0; i--) {
  158. const [start, end] = ranges[i];
  159. const startPath = path.get("body.body")[start];
  160. startPath.replaceWith(buildFieldsReplacement(path.node.body.body.slice(start, end), path.scope, this.file));
  161. for (let j = end - 1; j > start; j--) {
  162. path.get("body.body")[j].remove();
  163. }
  164. }
  165. }
  166. }
  167. };
  168. });
  169. exports.default = index;
  170. //# sourceMappingURL=index.js.map