176 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| Object.defineProperty(exports, '__esModule', { value: true });
 | |
| 
 | |
| var core = require('@babel/core');
 | |
| var helperPluginUtils = require('@babel/helper-plugin-utils');
 | |
| 
 | |
| function isNameOrLength(key) {
 | |
|   if (core.types.isIdentifier(key)) {
 | |
|     return key.name === "name" || key.name === "length";
 | |
|   }
 | |
|   if (core.types.isStringLiteral(key)) {
 | |
|     return key.value === "name" || key.value === "length";
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| function isStaticFieldWithValue(node) {
 | |
|   return (core.types.isClassProperty(node) || core.types.isClassPrivateProperty(node)) && node.static && !!node.value;
 | |
| }
 | |
| const hasReferenceVisitor = {
 | |
|   ReferencedIdentifier(path, state) {
 | |
|     if (path.node.name === state.name) {
 | |
|       state.ref();
 | |
|       path.stop();
 | |
|     }
 | |
|   },
 | |
|   Scope(path, {
 | |
|     name
 | |
|   }) {
 | |
|     if (path.scope.hasOwnBinding(name)) {
 | |
|       path.skip();
 | |
|     }
 | |
|   }
 | |
| };
 | |
| function isReferenceOrThis(node, name) {
 | |
|   return core.types.isThisExpression(node) || name && core.types.isIdentifier(node, {
 | |
|     name
 | |
|   });
 | |
| }
 | |
| const hasReferenceOrThisVisitor = {
 | |
|   "ThisExpression|ReferencedIdentifier"(path, state) {
 | |
|     if (isReferenceOrThis(path.node, state.name)) {
 | |
|       state.ref();
 | |
|       path.stop();
 | |
|     }
 | |
|   },
 | |
|   FunctionParent(path, state) {
 | |
|     if (path.isArrowFunctionExpression()) return;
 | |
|     if (state.name && !path.scope.hasOwnBinding(state.name)) {
 | |
|       path.traverse(hasReferenceVisitor, state);
 | |
|     }
 | |
|     path.skip();
 | |
|     if (path.isMethod()) {
 | |
|       if (path.requeueComputedKeyAndDecorators) {
 | |
|         path.requeueComputedKeyAndDecorators();
 | |
|       } else {
 | |
|         require("@babel/traverse").NodePath.prototype.requeueComputedKeyAndDecorators.call(path);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| function getPotentiallyBuggyFieldsIndexes(path) {
 | |
|   var _path$node$id;
 | |
|   const buggyPublicStaticFieldsIndexes = [];
 | |
|   let classReferenced = false;
 | |
|   const className = (_path$node$id = path.node.id) == null ? void 0 : _path$node$id.name;
 | |
|   const hasReferenceState = {
 | |
|     name: className,
 | |
|     ref: () => classReferenced = true
 | |
|   };
 | |
|   if (className) {
 | |
|     for (const el of path.get("body.body")) {
 | |
|       if (el.node.computed) {
 | |
|         el.get("key").traverse(hasReferenceVisitor, hasReferenceState);
 | |
|         if (classReferenced) break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   let nextPotentiallyBuggy = false;
 | |
|   const {
 | |
|     body
 | |
|   } = path.node.body;
 | |
|   for (let i = 0; i < body.length; i++) {
 | |
|     const node = body[i];
 | |
|     if (!nextPotentiallyBuggy) {
 | |
|       if (core.types.isStaticBlock(node)) {
 | |
|         classReferenced = true;
 | |
|         nextPotentiallyBuggy = true;
 | |
|       } else if (isStaticFieldWithValue(node)) {
 | |
|         if (!classReferenced) {
 | |
|           if (isReferenceOrThis(node.value, className)) {
 | |
|             classReferenced = true;
 | |
|           } else {
 | |
|             path.get(`body.body.${i}.value`).traverse(hasReferenceOrThisVisitor, hasReferenceState);
 | |
|           }
 | |
|         }
 | |
|         if (classReferenced) {
 | |
|           nextPotentiallyBuggy = !path.scope.isPure(node.value);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (core.types.isClassProperty(node, {
 | |
|       static: true
 | |
|     }) && (nextPotentiallyBuggy || node.computed || isNameOrLength(node.key))) {
 | |
|       buggyPublicStaticFieldsIndexes.push(i);
 | |
|     }
 | |
|   }
 | |
|   return buggyPublicStaticFieldsIndexes;
 | |
| }
 | |
| function getNameOrLengthStaticFieldsIndexes(path) {
 | |
|   const indexes = [];
 | |
|   const {
 | |
|     body
 | |
|   } = path.node.body;
 | |
|   for (let i = 0; i < body.length; i++) {
 | |
|     const node = body[i];
 | |
|     if (core.types.isClassProperty(node, {
 | |
|       static: true,
 | |
|       computed: false
 | |
|     }) && isNameOrLength(node.key)) {
 | |
|       indexes.push(i);
 | |
|     }
 | |
|   }
 | |
|   return indexes;
 | |
| }
 | |
| function toRanges(nums) {
 | |
|   const ranges = [];
 | |
|   if (nums.length === 0) return ranges;
 | |
|   let start = nums[0];
 | |
|   let end = start + 1;
 | |
|   for (let i = 1; i < nums.length; i++) {
 | |
|     if (nums[i] <= nums[i - 1]) {
 | |
|       throw new Error("Internal Babel error: nums must be in ascending order");
 | |
|     }
 | |
|     if (nums[i] === end) {
 | |
|       end++;
 | |
|     } else {
 | |
|       ranges.push([start, end]);
 | |
|       start = nums[i];
 | |
|       end = start + 1;
 | |
|     }
 | |
|   }
 | |
|   ranges.push([start, end]);
 | |
|   return ranges;
 | |
| }
 | |
| 
 | |
| function buildFieldsReplacement(fields, scope, file) {
 | |
|   return core.types.staticBlock(fields.map(field => {
 | |
|     const key = field.computed || !core.types.isIdentifier(field.key) ? field.key : core.types.stringLiteral(field.key.name);
 | |
|     return core.types.expressionStatement(core.types.callExpression(file.addHelper("defineProperty"), [core.types.thisExpression(), key, field.value || scope.buildUndefinedNode()]));
 | |
|   }));
 | |
| }
 | |
| var index = helperPluginUtils.declare(api => {
 | |
|   api.assertVersion("^7.0.0-0 || >8.0.0-alpha <8.0.0-beta");
 | |
|   const setPublicClassFields = api.assumption("setPublicClassFields");
 | |
|   return {
 | |
|     name: "bugfix-v8-static-class-fields-redefine-readonly",
 | |
|     visitor: {
 | |
|       Class(path) {
 | |
|         const ranges = toRanges(setPublicClassFields ? getNameOrLengthStaticFieldsIndexes(path) : getPotentiallyBuggyFieldsIndexes(path));
 | |
|         for (let i = ranges.length - 1; i >= 0; i--) {
 | |
|           const [start, end] = ranges[i];
 | |
|           const startPath = path.get("body.body")[start];
 | |
|           startPath.replaceWith(buildFieldsReplacement(path.node.body.body.slice(start, end), path.scope, this.file));
 | |
|           for (let j = end - 1; j > start; j--) {
 | |
|             path.get("body.body")[j].remove();
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| });
 | |
| 
 | |
| exports.default = index;
 | |
| //# sourceMappingURL=index.js.map
 |