172 lines
5.1 KiB
JavaScript
Executable file
172 lines
5.1 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
var core = require('@babel/core');
|
|
var helperPluginUtils = require('@babel/helper-plugin-utils');
|
|
var helperEnvironmentVisitor = require('@babel/helper-environment-visitor');
|
|
|
|
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()) {
|
|
helperEnvironmentVisitor.requeueComputedKeyAndDecorators(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);
|
|
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
|