diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index ae0fcf5cc8..2f1b5dd25c 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -735,6 +735,7 @@ helpers.possibleConstructorReturn = helper("7.0.0-beta.0")` } `; +// This is duplicated to packages/babel-plugin-transform-classes/src/inline-createSuper-helpers.js helpers.createSuper = helper("7.9.0")` import getPrototypeOf from "getPrototypeOf"; import isNativeReflectConstruct from "isNativeReflectConstruct"; diff --git a/packages/babel-plugin-transform-classes/src/inline-createSuper-helpers.js b/packages/babel-plugin-transform-classes/src/inline-createSuper-helpers.js new file mode 100644 index 0000000000..14ea844ed5 --- /dev/null +++ b/packages/babel-plugin-transform-classes/src/inline-createSuper-helpers.js @@ -0,0 +1,72 @@ +import { template, types as t } from "@babel/core"; + +const helperIDs = new WeakMap(); + +export default function addCreateSuperHelper(file) { + try { + return file.addHelper("createSuper"); + } catch { + // Babel <7.9.0 doesn't support the helper. + } + + if (helperIDs.has(file)) { + // TODO: Only use t.cloneNode in Babel 8 + // t.cloneNode isn't supported in every version + return (t.cloneNode || t.clone)(helperIDs.get(file)); + } + + const id = file.scope.generateUidIdentifier("createSuper"); + helperIDs.set(file, id); + + const fn = helper({ + CREATE_SUPER: id, + GET_PROTOTYPE_OF: file.addHelper("getPrototypeOf"), + POSSIBLE_CONSTRUCTOR_RETURN: file.addHelper("possibleConstructorReturn"), + }); + + file.path.unshiftContainer("body", [fn]); + file.scope.registerDeclaration(file.path.get("body.0")); + + return t.cloneNode(id); +} + +const helper = template.statement` + function CREATE_SUPER(Derived) { + function isNativeReflectConstruct() { + if (typeof Reflect === "undefined" || !Reflect.construct) return false; + + // core-js@3 + if (Reflect.construct.sham) return false; + + // Proxy can't be polyfilled. Every browser implemented + // proxies before or at the same time as Reflect.construct, + // so if they support Proxy they also support Reflect.construct. + if (typeof Proxy === "function") return true; + + // Since Reflect.construct can't be properly polyfilled, some + // implementations (e.g. core-js@2) don't set the correct internal slots. + // Those polyfills don't allow us to subclass built-ins, so we need to + // use our fallback implementation. + try { + // If the internal slots aren't set, this throws an error similar to + // TypeError: this is not a Date object. + Date.prototype.toString.call(Reflect.construct(Date, [], function() {})); + return true; + } catch (e) { + return false; + } + } + + return function () { + var Super = GET_PROTOTYPE_OF(Derived), result; + if (isNativeReflectConstruct()) { + // NOTE: This doesn't work if this.__proto__.constructor has been modified. + var NewTarget = GET_PROTOTYPE_OF(this).constructor; + result = Reflect.construct(Super, arguments, NewTarget); + } else { + result = Super.apply(this, arguments); + } + return POSSIBLE_CONSTRUCTOR_RETURN(this, result); + } + } +`; diff --git a/packages/babel-plugin-transform-classes/src/transformClass.js b/packages/babel-plugin-transform-classes/src/transformClass.js index 86040d39bd..c4a396b432 100644 --- a/packages/babel-plugin-transform-classes/src/transformClass.js +++ b/packages/babel-plugin-transform-classes/src/transformClass.js @@ -8,6 +8,8 @@ import * as defineMap from "@babel/helper-define-map"; import { traverse, template, types as t } from "@babel/core"; import annotateAsPure from "@babel/helper-annotate-as-pure"; +import addCreateSuperHelper from "./inline-createSuper-helpers"; + type ReadonlySet = Set | { has(val: T): boolean }; function buildConstructor(classRef, constructorBody, node) { @@ -556,7 +558,7 @@ export default function transformClass( t.variableDeclaration("var", [ t.variableDeclarator( superFnId, - t.callExpression(classState.file.addHelper("createSuper"), [ + t.callExpression(addCreateSuperHelper(classState.file), [ t.cloneNode(classState.classRef), ]), ),