diff --git a/babel.config.js b/babel.config.js index f595e3a4e5..c66b077a8a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -194,7 +194,10 @@ module.exports = function (api) { assumptions: parserAssumptions, }, { - test: ["packages/babel-generator"].map(normalize), + test: [ + "packages/babel-generator", + "packages/babel-plugin-proposal-decorators", + ].map(normalize), plugins: ["babel-plugin-transform-charcodes"], }, convertESM && { diff --git a/packages/babel-plugin-proposal-decorators/src/index.ts b/packages/babel-plugin-proposal-decorators/src/index.ts index ddf98ea99e..1a3b6c4a3d 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.ts +++ b/packages/babel-plugin-proposal-decorators/src/index.ts @@ -37,9 +37,11 @@ export default declare((api, options) => { } } - if (legacy) { - if (version !== undefined) { - throw new Error("'version' can't be used with legacy decorators"); + if (version === "legacy" || legacy) { + if (version !== undefined && legacy) { + throw new Error( + 'You can either specify `legacy: true` or `version: "legacy"` with decorators, not both.', + ); } return { diff --git a/packages/babel-plugin-proposal-decorators/src/transformer-2021-12.ts b/packages/babel-plugin-proposal-decorators/src/transformer-2021-12.ts index 510dbd4d86..95872e23a9 100644 --- a/packages/babel-plugin-proposal-decorators/src/transformer-2021-12.ts +++ b/packages/babel-plugin-proposal-decorators/src/transformer-2021-12.ts @@ -40,20 +40,16 @@ function incrementId(id: number[], idx = id.length - 1): void { } /** - * Generates a new element name that is unique to the given class. This can be + * Generates a new private name that is unique to the given class. This can be * used to create extra class fields and methods for the implementation, while * keeping the length of those names as small as possible. This is important for - * minification purposes, since public names cannot be safely renamed/minified. - * - * Names are split into two namespaces, public and private. Static and non-static - * names are shared in the same namespace, because this is true for private names - * (you cannot have #x and static #x in the same class) and it's not worth the - * extra complexity for public names. + * minification purposes (though private names can generally be minified, + * transpilations and polyfills cannot yet). */ function createPrivateUidGeneratorForClass( classPath: NodePath, ): () => t.PrivateName { - const currentPrivateId = [charCodes.uppercaseA]; + const currentPrivateId = []; const privateNames = new Set(); classPath.traverse({ @@ -63,14 +59,11 @@ function createPrivateUidGeneratorForClass( }); return (): t.PrivateName => { - let reifiedId = String.fromCharCode(...currentPrivateId); - - while (privateNames.has(reifiedId)) { + let reifiedId; + do { incrementId(currentPrivateId); reifiedId = String.fromCharCode(...currentPrivateId); - } - - incrementId(currentPrivateId); + } while (privateNames.has(reifiedId)); return t.privateName(t.identifier(reifiedId)); }; @@ -124,16 +117,17 @@ function replaceClassWithVar( if (path.node.id) { className = path.node.id.name; - varId = generateLocalVarId(path, className); + varId = path.scope.parent.generateDeclaredUidIdentifier(className); path.scope.rename(className, varId.name); } else if ( path.parentPath.node.type === "VariableDeclarator" && path.parentPath.node.id.type === "Identifier" ) { className = path.parentPath.node.id.name; - varId = generateLocalVarId(path, className); + varId = path.scope.parent.generateDeclaredUidIdentifier(className); } else { - varId = generateLocalVarId(path, "decorated_class"); + varId = + path.scope.parent.generateDeclaredUidIdentifier("decorated_class"); } const newClassExpr = t.classExpression( @@ -283,12 +277,6 @@ function getElementKind(element: NodePath): number { } } -function generateLocalVarId(path: NodePath, name: string): t.Identifier { - const varId = path.scope.generateUidIdentifier(name); - path.scope.parent.push({ id: varId }); - return t.cloneNode(varId); -} - // Information about the decorators applied to an element interface DecoratorInfo { // The expressions of the decorators themselves @@ -514,7 +502,8 @@ function transformClass( classLocal: t.Identifier; if (classDecorators) { - classInitLocal = generateLocalVarId(path, "initClass"); + classInitLocal = + path.scope.parent.generateDeclaredUidIdentifier("initClass"); const [localId, classPath] = replaceClassWithVar(path); path = classPath; @@ -560,7 +549,8 @@ function transformClass( if (isComputed) { const keyPath = element.get("key"); - const localComputedNameId = generateLocalVarId(keyPath, name); + const localComputedNameId = + keyPath.scope.parent.generateDeclaredUidIdentifier(name); keyPath.replaceWith(localComputedNameId); elementDecoratorInfo.push({ @@ -586,7 +576,8 @@ function transformClass( } const newId = generateClassPrivateUid(); - const newFieldInitId = generateLocalVarId(element, `init_${name}`); + const newFieldInitId = + element.scope.parent.generateDeclaredUidIdentifier(`init_${name}`); const newValue = t.callExpression( t.cloneNode(newFieldInitId), params, @@ -598,8 +589,12 @@ function transformClass( if (isPrivate) { privateMethods = extractProxyAccessorsFor(newId); - const getId = generateLocalVarId(newPath, `get_${name}`); - const setId = generateLocalVarId(newPath, `set_${name}`); + const getId = newPath.scope.parent.generateDeclaredUidIdentifier( + `get_${name}`, + ); + const setId = newPath.scope.parent.generateDeclaredUidIdentifier( + `set_${name}`, + ); addCallAccessorsFor(newPath, key as t.PrivateName, getId, setId); @@ -609,7 +604,9 @@ function transformClass( locals = newFieldInitId; } } else if (kind === FIELD) { - const initId = generateLocalVarId(element, `init_${name}`); + const initId = element.scope.parent.generateDeclaredUidIdentifier( + `init_${name}`, + ); const valuePath = ( element as NodePath ).get("value"); @@ -627,7 +624,9 @@ function transformClass( privateMethods = extractProxyAccessorsFor(key as t.PrivateName); } } else if (isPrivate) { - locals = generateLocalVarId(element, `call_${name}`); + locals = element.scope.parent.generateDeclaredUidIdentifier( + `call_${name}`, + ) as t.Identifier; const replaceSupers = new ReplaceSupers({ constantSuper, @@ -730,7 +729,8 @@ function transformClass( const newDecorators: t.Identifier[] = []; for (const decorator of decorators) { - const localComputedNameId = generateLocalVarId(path, "dec"); + const localComputedNameId = + path.scope.parent.generateDeclaredUidIdentifier("dec"); assignments.push( t.assignmentExpression("=", localComputedNameId, decorator), ); @@ -761,7 +761,8 @@ function transformClass( } if (requiresProtoInit) { - protoInitLocal = generateLocalVarId(path, "initProto"); + protoInitLocal = + path.scope.parent.generateDeclaredUidIdentifier("initProto"); locals.push(protoInitLocal); const protoInitCall = t.callExpression(t.cloneNode(protoInitLocal), [ @@ -789,7 +790,8 @@ function transformClass( found = true; - const prop = generateLocalVarId(path, "super"); + const prop = + path.scope.parent.generateDeclaredUidIdentifier("super"); parentPath.replaceWith( t.sequenceExpression([ t.assignmentExpression("=", t.cloneNode(prop), parentPath.node), @@ -829,7 +831,8 @@ function transformClass( } if (requiresStaticInit) { - staticInitLocal = generateLocalVarId(path, "initStatic"); + staticInitLocal = + path.scope.parent.generateDeclaredUidIdentifier("initStatic"); locals.push(staticInitLocal); }