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.
172 lines
5.1 KiB
172 lines
5.1 KiB
'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
|
|
|