UNPKG

@babel/helper-create-class-features-plugin

Version:

Compile class public and private fields, private methods and decorators to ES6

1 lines 144 kB
{"version":3,"names":["_core","require","_helperReplaceSupers","_helperSkipTransparentExpressionWrappers","_fields","_misc","incrementId","id","idx","length","unshift","current","createPrivateUidGeneratorForClass","classPath","currentPrivateId","privateNames","Set","traverse","PrivateName","path","add","node","name","reifiedId","String","fromCharCode","has","t","privateName","identifier","createLazyPrivateUidGeneratorForClass","generator","replaceClassWithVar","className","scope","type","varId","generateUidIdentifierBasedOnNode","classId","rename","get","replaceWith","cloneNode","generateLetUidIdentifier","parent","newClassExpr","classExpression","superClass","body","newPath","sequenceExpression","generateClassProperty","key","value","isStatic","classPrivateProperty","undefined","classProperty","assignIdForAnonymousClass","generateUidIdentifier","addProxyAccessorsFor","element","getterKey","setterKey","targetKey","isComputed","version","thisArg","thisExpression","getterBody","blockStatement","returnStatement","memberExpression","setterBody","expressionStatement","assignmentExpression","getter","setter","classPrivateMethod","classMethod","insertAfter","extractProxyAccessorsFor","template","expression","ast","getComputedKeyLastElement","skipTransparentExprWrappers","isSequenceExpression","expressions","getComputedKeyMemoiser","isConstantExpression","isIdentifier","hasUid","isAssignmentExpression","left","Error","toString","prependExpressionsToComputedKey","fieldPath","push","maybeSequenceExpression","appendExpressionsToComputedKey","completion","scopeParent","maybeAssignment","memoiseComputedKey","generateUid","expressionSequence","completionParent","parentPath","pushContainer","prependExpressionsToFieldInitializer","initializer","unaryExpression","prependExpressionsToStaticBlock","blockPath","unshiftContainer","prependExpressionsToConstructor","constructorPath","isProtoInitCallExpression","protoInitCall","isCallExpression","callee","optimizeSuperCallAndExpressions","protoInitLocal","mergedSuperCall","callExpression","splice","isThisExpression","insertExpressionsAfterSuperCallAndOptimize","CallExpression","exit","isSuper","newNodes","map","expr","isCompletionRecord","skip","ClassMethod","kind","createConstructorFromExpressions","isDerivedClass","super","spreadElement","restElement","createStaticBlockFromExpressions","staticBlock","FIELD","ACCESSOR","METHOD","GETTER","SETTER","STATIC_OLD_VERSION","STATIC","DECORATORS_HAVE_THIS","getElementKind","toSortedDecoratorInfo","info","filter","el","generateDecorationList","decorators","decoratorsThis","decsCount","haveOneThis","some","Boolean","decs","i","numericLiteral","haveThis","generateDecorationExprs","decorationInfo","arrayExpression","flag","decoratorsHaveThis","decoratorsArray","privateMethods","extractElementLocalAssignments","localIds","locals","Array","isArray","addCallAccessorsFor","getId","setId","movePrivateAccessor","methodLocalVar","params","block","isClassDecoratableElementPath","staticBlockToIIFE","arrowFunctionExpression","staticBlockToFunctionClosure","functionExpression","fieldInitializerToClosure","exprs","createFunctionExpressionFromPrivateMethod","isGenerator","async","isAsync","createSetFunctionNameCall","state","addHelper","createToPropertyKeyCall","propertyKey","createPrivateBrandCheckClosure","brandName","binaryExpression","usesPrivateField","traverseFast","isPrivateName","_unused","convertToComputedKey","computed","stringLiteral","hasInstancePrivateAccess","containsInstancePrivateAccess","privateNameVisitor","privateNameVisitorFactory","privateNamesMap","stop","Map","set","checkPrivateMethodUpdateError","decoratedPrivateMethods","parentParentPath","buildCodeFrameError","transformClass","constantSuper","ignoreFunctionLength","propertyVisitor","_path$node$id","_classDecorationsId","classDecorators","hasElementDecorators","hasComputedKeysSideEffects","elemDecsUseFnContext","generateClassPrivateUid","classAssignments","memoiseExpression","hint","assignments","localEvaluatedId","staticInitLocal","classIdName","setClassName","usesFunctionContextOrYieldAwait","decorator","isYieldExpression","isAwaitExpression","isMetaProperty","meta","_unused2","instancePrivateNames","elementNode","static","isDecorated","ClassProperty","ClassPrivateProperty","ClassAccessorProperty","_staticInitLocal","_protoInitLocal","newId","newField","keyPath","elementDecoratorInfo","classInitLocal","classIdLocal","decoratorReceiverId","handleDecorators","hasSideEffects","usesFnContext","object","isMemberExpression","_decoratorReceiverId","willExtractSomeElemDecs","needsDeclaraionForClassBinding","classDecorationsFlag","classDecorations","classDecorationsId","computedKeyAssignments","isClassDeclaration","classDecsUsePrivateName","isClassProperty","lastInstancePrivateName","needsInstancePrivateBrandCheck","fieldInitializerExpressions","staticFieldInitializerExpressions","isStaticBlock","hasDecorators","isPrivate","isClassPrivateProperty","isClassMethod","nameExpr","newFieldInitId","newValue","initId","valuePath","args","callId","replaceSupers","ReplaceSupers","methodPath","objectRef","superRef","file","refToPreserve","replace","remove","getNextSibling","initExtraId","initExtraCall","elements","lastComputedElement","sortedElementDecoratorInfo","elementDecorations","elementLocals","classLocals","classInitInjected","classInitCall","originalClassPath","originalClass","staticClosures","statics","forEach","staticBlockClosureId","fieldValueClosureId","isClassPrivateMethod","privateMethodDelegateId","p","isRestElement","staticsClass","toExpression","constructorBody","newExpr","newExpression","arguments","maybeGenerateMemoised","applyDecoratorWrapper","applyDecsBody","firstPublicElement","createLocalsAssignment","insertBefore","classBindingInfo","getBinding","constantViolations","variableDeclaration","variableDeclarator","classOuterBindingDelegateLocal","classOuterBindingLocal","replaceWithMultiple","size","crawl","maybePrivateBrandName","lhs","rhs","availableHelper","arrayPattern","objectPattern","objectProperty","isProtoKey","shouldTransformElement","shouldTransformClass","NamedEvaluationVisitoryFactory","isAnonymous","visitor","handleComputedProperty","propertyPath","keyValue","ref","VariableDeclarator","AssignmentExpression","operator","AssignmentPattern","ObjectExpression","isObjectProperty","isDecoratedAnonymousClassExpression","isClassExpression","_default","assertVersion","assumption","loose","inherits","_assumption","_assumption2","VISITED","WeakSet","namedEvaluationVisitor","visitClass","_className","_node$id","Object","assign","ExportDefaultDeclaration","declaration","_path$splitExportDecl","splitExportDeclaration","NodePath","prototype","updatedVarDeclarationPath","ExportNamedDeclaration","_path$splitExportDecl2","Class"],"sources":["../src/decorators.ts"],"sourcesContent":["import type { NodePath, Scope, Visitor } from \"@babel/core\";\nimport { types as t, template } from \"@babel/core\";\nimport ReplaceSupers from \"@babel/helper-replace-supers\";\nimport type { PluginAPI, PluginObject, PluginPass } from \"@babel/core\";\nimport { skipTransparentExprWrappers } from \"@babel/helper-skip-transparent-expression-wrappers\";\nimport {\n privateNameVisitorFactory,\n type PrivateNameVisitorState,\n} from \"./fields.ts\";\nimport { memoiseComputedKey } from \"./misc.ts\";\n\n// We inline this package\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport * as charCodes from \"charcodes\";\ninterface Options {\n /** @deprecated use `constantSuper` assumption instead. Only supported in 2021-12 version. */\n loose?: boolean;\n}\n\ntype ClassDecoratableElement =\n | t.ClassMethod\n | t.ClassPrivateMethod\n | t.ClassProperty\n | t.ClassPrivateProperty\n | t.ClassAccessorProperty;\n\ntype ClassElement =\n | ClassDecoratableElement\n | t.TSDeclareMethod\n | t.TSIndexSignature\n | t.StaticBlock;\n\ntype ClassElementCanHaveComputedKeys =\n | t.ClassMethod\n | t.ClassProperty\n | t.ClassAccessorProperty;\n\n// TODO(Babel 8): Only keep 2023-11\nexport type DecoratorVersionKind =\n | \"2023-11\"\n | \"2023-05\"\n | \"2023-01\"\n | \"2022-03\"\n | \"2021-12\";\n\nfunction incrementId(id: number[], idx = id.length - 1): void {\n // If index is -1, id needs an additional character, unshift A\n if (idx === -1) {\n id.unshift(charCodes.uppercaseA);\n return;\n }\n\n const current = id[idx];\n\n if (current === charCodes.uppercaseZ) {\n // if current is Z, skip to a\n id[idx] = charCodes.lowercaseA;\n } else if (current === charCodes.lowercaseZ) {\n // if current is z, reset to A and carry the 1\n id[idx] = charCodes.uppercaseA;\n incrementId(id, idx - 1);\n } else {\n // else, increment by one\n id[idx] = current + 1;\n }\n}\n\n/**\n * Generates a new private name that is unique to the given class. This can be\n * used to create extra class fields and methods for the implementation, while\n * keeping the length of those names as small as possible. This is important for\n * minification purposes (though private names can generally be minified,\n * transpilations and polyfills cannot yet).\n */\nfunction createPrivateUidGeneratorForClass(\n classPath: NodePath<t.ClassDeclaration | t.ClassExpression>,\n): () => t.PrivateName {\n const currentPrivateId: number[] = [];\n const privateNames = new Set<string>();\n\n classPath.traverse({\n PrivateName(path) {\n privateNames.add(path.node.id.name);\n },\n });\n\n return (): t.PrivateName => {\n let reifiedId;\n do {\n incrementId(currentPrivateId);\n reifiedId = String.fromCharCode(...currentPrivateId);\n } while (privateNames.has(reifiedId));\n\n return t.privateName(t.identifier(reifiedId));\n };\n}\n\n/**\n * Wraps the above generator function so that it's run lazily the first time\n * it's actually required. Several types of decoration do not require this, so it\n * saves iterating the class elements an additional time and allocating the space\n * for the Sets of element names.\n */\nfunction createLazyPrivateUidGeneratorForClass(\n classPath: NodePath<t.ClassDeclaration | t.ClassExpression>,\n): () => t.PrivateName {\n let generator: () => t.PrivateName;\n\n return (): t.PrivateName => {\n if (!generator) {\n generator = createPrivateUidGeneratorForClass(classPath);\n }\n\n return generator();\n };\n}\n\n/**\n * Takes a class definition and the desired class name if anonymous and\n * replaces it with an equivalent class declaration (path) which is then\n * assigned to a local variable (id). This allows us to reassign the local variable with the\n * decorated version of the class. The class definition retains its original\n * name so that `toString` is not affected, other references to the class\n * are renamed instead.\n */\nfunction replaceClassWithVar(\n path: NodePath<t.ClassDeclaration | t.ClassExpression>,\n className: string | t.Identifier | t.StringLiteral | undefined,\n): {\n id: t.Identifier;\n path: NodePath<t.ClassDeclaration | t.ClassExpression>;\n} {\n const id = path.node.id;\n const scope = path.scope;\n if (path.type === \"ClassDeclaration\") {\n const className = id.name;\n const varId = scope.generateUidIdentifierBasedOnNode(id);\n const classId = t.identifier(className);\n\n scope.rename(className, varId.name);\n\n path.get(\"id\").replaceWith(classId);\n\n return { id: t.cloneNode(varId), path };\n } else {\n let varId: t.Identifier;\n\n if (id) {\n className = id.name;\n varId = generateLetUidIdentifier(scope.parent, className);\n scope.rename(className, varId.name);\n } else {\n varId = generateLetUidIdentifier(\n scope.parent,\n typeof className === \"string\" ? className : \"decorated_class\",\n );\n }\n\n const newClassExpr = t.classExpression(\n typeof className === \"string\" ? t.identifier(className) : null,\n path.node.superClass,\n path.node.body,\n );\n\n const [newPath] = path.replaceWith(\n t.sequenceExpression([newClassExpr, varId]),\n );\n\n return {\n id: t.cloneNode(varId),\n path: newPath.get(\"expressions.0\") as NodePath<t.ClassExpression>,\n };\n }\n}\n\nfunction generateClassProperty(\n key: t.PrivateName | t.Identifier,\n value: t.Expression | undefined,\n isStatic: boolean,\n): t.ClassPrivateProperty | t.ClassProperty {\n if (key.type === \"PrivateName\") {\n return t.classPrivateProperty(key, value, undefined, isStatic);\n } else {\n return t.classProperty(key, value, undefined, undefined, isStatic);\n }\n}\n\nfunction assignIdForAnonymousClass(\n path: NodePath<t.Class>,\n className: string | t.Identifier | t.StringLiteral | undefined,\n) {\n if (!path.node.id) {\n path.node.id =\n typeof className === \"string\"\n ? t.identifier(className)\n : path.scope.generateUidIdentifier(\"Class\");\n }\n}\n\nfunction addProxyAccessorsFor(\n className: t.Identifier,\n element: NodePath<ClassDecoratableElement>,\n getterKey: t.PrivateName | t.Expression,\n setterKey: t.PrivateName | t.Expression,\n targetKey: t.PrivateName,\n isComputed: boolean,\n isStatic: boolean,\n version: DecoratorVersionKind,\n): void {\n const thisArg =\n (version === \"2023-11\" ||\n (!process.env.BABEL_8_BREAKING && version === \"2023-05\")) &&\n isStatic\n ? className\n : t.thisExpression();\n\n const getterBody = t.blockStatement([\n t.returnStatement(\n t.memberExpression(t.cloneNode(thisArg), t.cloneNode(targetKey)),\n ),\n ]);\n\n const setterBody = t.blockStatement([\n t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.cloneNode(thisArg), t.cloneNode(targetKey)),\n t.identifier(\"v\"),\n ),\n ),\n ]);\n\n let getter: t.ClassMethod | t.ClassPrivateMethod,\n setter: t.ClassMethod | t.ClassPrivateMethod;\n\n if (getterKey.type === \"PrivateName\") {\n getter = t.classPrivateMethod(\"get\", getterKey, [], getterBody, isStatic);\n setter = t.classPrivateMethod(\n \"set\",\n setterKey as t.PrivateName,\n [t.identifier(\"v\")],\n setterBody,\n isStatic,\n );\n } else {\n getter = t.classMethod(\n \"get\",\n getterKey,\n [],\n getterBody,\n isComputed,\n isStatic,\n );\n setter = t.classMethod(\n \"set\",\n setterKey as t.Expression,\n [t.identifier(\"v\")],\n setterBody,\n isComputed,\n isStatic,\n );\n }\n\n element.insertAfter(setter);\n element.insertAfter(getter);\n}\n\nfunction extractProxyAccessorsFor(\n targetKey: t.PrivateName,\n version: DecoratorVersionKind,\n): (t.FunctionExpression | t.ArrowFunctionExpression)[] {\n if (version !== \"2023-11\" && version !== \"2023-05\" && version !== \"2023-01\") {\n return [\n template.expression.ast`\n function () {\n return this.${t.cloneNode(targetKey)};\n }\n ` as t.FunctionExpression,\n template.expression.ast`\n function (value) {\n this.${t.cloneNode(targetKey)} = value;\n }\n ` as t.FunctionExpression,\n ];\n }\n return [\n template.expression.ast`\n o => o.${t.cloneNode(targetKey)}\n ` as t.ArrowFunctionExpression,\n template.expression.ast`\n (o, v) => o.${t.cloneNode(targetKey)} = v\n ` as t.ArrowFunctionExpression,\n ];\n}\n\n/**\n * Get the last element for the given computed key path.\n *\n * This function unwraps transparent wrappers and gets the last item when\n * the key is a SequenceExpression.\n *\n * @param {NodePath<t.Expression>} path The key of a computed class element\n * @returns {NodePath<t.Expression>} The simple completion result\n */\nfunction getComputedKeyLastElement(\n path: NodePath<t.Expression>,\n): NodePath<t.Expression> {\n path = skipTransparentExprWrappers(path);\n if (path.isSequenceExpression()) {\n const expressions = path.get(\"expressions\");\n return getComputedKeyLastElement(expressions[expressions.length - 1]);\n }\n return path;\n}\n\n/**\n * Get a memoiser of the computed key path.\n *\n * This function does not mutate AST. If the computed key is not a constant\n * expression, this function must be called after the key has been memoised.\n *\n * @param {NodePath<t.Expression>} path The key of a computed class element.\n * @returns {t.Expression} A clone of key if key is a constant expression,\n * otherwise a memoiser identifier.\n */\nfunction getComputedKeyMemoiser(path: NodePath<t.Expression>): t.Expression {\n const element = getComputedKeyLastElement(path);\n if (element.isConstantExpression()) {\n return t.cloneNode(path.node);\n } else if (element.isIdentifier() && path.scope.hasUid(element.node.name)) {\n return t.cloneNode(path.node);\n } else if (\n element.isAssignmentExpression() &&\n element.get(\"left\").isIdentifier()\n ) {\n return t.cloneNode(element.node.left as t.Identifier);\n } else {\n throw new Error(\n `Internal Error: the computed key ${path.toString()} has not yet been memoised.`,\n );\n }\n}\n\n/**\n * Prepend expressions to the computed key of the given field path.\n *\n * If the computed key is a sequence expression, this function will unwrap\n * the sequence expression for optimal output size.\n *\n * @param {t.Expression[]} expressions\n * @param {(NodePath<\n * t.ClassMethod | t.ClassProperty | t.ClassAccessorProperty\n * >)} fieldPath\n */\nfunction prependExpressionsToComputedKey(\n expressions: t.Expression[],\n fieldPath: NodePath<\n t.ClassMethod | t.ClassProperty | t.ClassAccessorProperty\n >,\n) {\n const key = fieldPath.get(\"key\") as NodePath<t.Expression>;\n if (key.isSequenceExpression()) {\n expressions.push(...key.node.expressions);\n } else {\n expressions.push(key.node);\n }\n key.replaceWith(maybeSequenceExpression(expressions));\n}\n\n/**\n * Append expressions to the computed key of the given field path.\n *\n * If the computed key is a constant expression or uid reference, it\n * will prepend expressions before the comptued key. Otherwise it will\n * memoise the computed key to preserve its completion result.\n *\n * @param {t.Expression[]} expressions\n * @param {(NodePath<\n * t.ClassMethod | t.ClassProperty | t.ClassAccessorProperty\n * >)} fieldPath\n */\nfunction appendExpressionsToComputedKey(\n expressions: t.Expression[],\n fieldPath: NodePath<\n t.ClassMethod | t.ClassProperty | t.ClassAccessorProperty\n >,\n) {\n const key = fieldPath.get(\"key\") as NodePath<t.Expression>;\n const completion = getComputedKeyLastElement(key);\n if (completion.isConstantExpression()) {\n prependExpressionsToComputedKey(expressions, fieldPath);\n } else {\n const scopeParent = key.scope.parent;\n const maybeAssignment = memoiseComputedKey(\n completion.node,\n scopeParent,\n scopeParent.generateUid(\"computedKey\"),\n );\n if (!maybeAssignment) {\n // If the memoiseComputedKey returns undefined, the key is already a uid reference,\n // treat it as a constant expression and prepend expressions before it\n prependExpressionsToComputedKey(expressions, fieldPath);\n } else {\n const expressionSequence = [\n ...expressions,\n // preserve the completion result\n t.cloneNode(maybeAssignment.left),\n ];\n const completionParent = completion.parentPath;\n if (completionParent.isSequenceExpression()) {\n completionParent.pushContainer(\"expressions\", expressionSequence);\n } else {\n completion.replaceWith(\n maybeSequenceExpression([\n t.cloneNode(maybeAssignment),\n ...expressionSequence,\n ]),\n );\n }\n }\n }\n}\n\n/**\n * Prepend expressions to the field initializer. If the initializer is not defined,\n * this function will wrap the last expression within a `void` unary expression.\n *\n * @param {t.Expression[]} expressions\n * @param {(NodePath<\n * t.ClassProperty | t.ClassPrivateProperty | t.ClassAccessorProperty\n * >)} fieldPath\n */\nfunction prependExpressionsToFieldInitializer(\n expressions: t.Expression[],\n fieldPath: NodePath<\n t.ClassProperty | t.ClassPrivateProperty | t.ClassAccessorProperty\n >,\n) {\n const initializer = fieldPath.get(\"value\");\n if (initializer.node) {\n expressions.push(initializer.node);\n } else if (expressions.length > 0) {\n expressions[expressions.length - 1] = t.unaryExpression(\n \"void\",\n expressions[expressions.length - 1],\n );\n }\n initializer.replaceWith(maybeSequenceExpression(expressions));\n}\n\nfunction prependExpressionsToStaticBlock(\n expressions: t.Expression[],\n blockPath: NodePath<t.StaticBlock>,\n) {\n blockPath.unshiftContainer(\n \"body\",\n t.expressionStatement(maybeSequenceExpression(expressions)),\n );\n}\n\nfunction prependExpressionsToConstructor(\n expressions: t.Expression[],\n constructorPath: NodePath<t.ClassMethod>,\n) {\n constructorPath.node.body.body.unshift(\n t.expressionStatement(maybeSequenceExpression(expressions)),\n );\n}\n\nfunction isProtoInitCallExpression(\n expression: t.Expression,\n protoInitCall: t.Identifier,\n) {\n return (\n t.isCallExpression(expression) &&\n t.isIdentifier(expression.callee, { name: protoInitCall.name })\n );\n}\n\n/**\n * Optimize super call and its following expressions\n *\n * @param {t.Expression[]} expressions Mutated by this function. The first element must by a super call\n * @param {t.Identifier} protoInitLocal The generated protoInit id\n * @returns optimized expression\n */\nfunction optimizeSuperCallAndExpressions(\n expressions: t.Expression[],\n protoInitLocal: t.Identifier,\n) {\n if (protoInitLocal) {\n if (\n expressions.length >= 2 &&\n isProtoInitCallExpression(expressions[1], protoInitLocal)\n ) {\n // Merge `super(), protoInit(this)` into `protoInit(super())`\n const mergedSuperCall = t.callExpression(t.cloneNode(protoInitLocal), [\n expressions[0],\n ]);\n expressions.splice(0, 2, mergedSuperCall);\n }\n // Merge `protoInit(super()), this` into `protoInit(super())`\n if (\n expressions.length >= 2 &&\n t.isThisExpression(expressions[expressions.length - 1]) &&\n isProtoInitCallExpression(\n expressions[expressions.length - 2],\n protoInitLocal,\n )\n ) {\n expressions.splice(expressions.length - 1, 1);\n }\n }\n return maybeSequenceExpression(expressions);\n}\n\n/**\n * Insert expressions immediately after super() and optimize the output if possible.\n * This function will preserve the completion result using the trailing this expression.\n *\n * @param {t.Expression[]} expressions\n * @param {NodePath<t.ClassMethod>} constructorPath\n * @param {t.Identifier} protoInitLocal The generated protoInit id\n * @returns\n */\nfunction insertExpressionsAfterSuperCallAndOptimize(\n expressions: t.Expression[],\n constructorPath: NodePath<t.ClassMethod>,\n protoInitLocal: t.Identifier,\n) {\n constructorPath.traverse({\n CallExpression: {\n exit(path) {\n if (!path.get(\"callee\").isSuper()) return;\n const newNodes = [\n path.node,\n ...expressions.map(expr => t.cloneNode(expr)),\n ];\n // preserve completion result if super() is in an RHS or a return statement\n if (path.isCompletionRecord()) {\n newNodes.push(t.thisExpression());\n }\n path.replaceWith(\n optimizeSuperCallAndExpressions(newNodes, protoInitLocal),\n );\n\n path.skip();\n },\n },\n ClassMethod(path) {\n if (path.node.kind === \"constructor\") {\n path.skip();\n }\n },\n });\n}\n\n/**\n * Build a class constructor node from the given expressions. If the class is\n * derived, the constructor will call super() first to ensure that `this`\n * in the expressions work as expected.\n *\n * @param {t.Expression[]} expressions\n * @param {boolean} isDerivedClass\n * @returns The class constructor node\n */\nfunction createConstructorFromExpressions(\n expressions: t.Expression[],\n isDerivedClass: boolean,\n) {\n const body: t.Statement[] = [\n t.expressionStatement(maybeSequenceExpression(expressions)),\n ];\n if (isDerivedClass) {\n body.unshift(\n t.expressionStatement(\n t.callExpression(t.super(), [t.spreadElement(t.identifier(\"args\"))]),\n ),\n );\n }\n return t.classMethod(\n \"constructor\",\n t.identifier(\"constructor\"),\n isDerivedClass ? [t.restElement(t.identifier(\"args\"))] : [],\n t.blockStatement(body),\n );\n}\n\nfunction createStaticBlockFromExpressions(expressions: t.Expression[]) {\n return t.staticBlock([\n t.expressionStatement(maybeSequenceExpression(expressions)),\n ]);\n}\n\n// 3 bits reserved to this (0-7)\nconst FIELD = 0;\nconst ACCESSOR = 1;\nconst METHOD = 2;\nconst GETTER = 3;\nconst SETTER = 4;\n\nconst STATIC_OLD_VERSION = 5; // Before 2023-05\nconst STATIC = 8; // 1 << 3\nconst DECORATORS_HAVE_THIS = 16; // 1 << 4\n\nfunction getElementKind(element: NodePath<ClassDecoratableElement>): number {\n switch (element.node.type) {\n case \"ClassProperty\":\n case \"ClassPrivateProperty\":\n return FIELD;\n case \"ClassAccessorProperty\":\n return ACCESSOR;\n case \"ClassMethod\":\n case \"ClassPrivateMethod\":\n if (element.node.kind === \"get\") {\n return GETTER;\n } else if (element.node.kind === \"set\") {\n return SETTER;\n } else {\n return METHOD;\n }\n }\n}\n\n// Information about the decorators applied to an element\ninterface DecoratorInfo {\n // An array of applied decorators or a memoised identifier\n decoratorsArray: t.Identifier | t.ArrayExpression | t.Expression;\n decoratorsHaveThis: boolean;\n\n // The kind of the decorated value, matches the kind value passed to applyDecs\n kind: number;\n\n // whether or not the field is static\n isStatic: boolean;\n\n // The name of the decorator\n name: t.StringLiteral | t.Expression;\n\n privateMethods:\n | (t.FunctionExpression | t.ArrowFunctionExpression)[]\n | undefined;\n\n // The names of local variables that will be used/returned from the decoration\n locals: t.Identifier | t.Identifier[] | undefined;\n}\n\n/**\n * Sort decoration info in the application order:\n * - static non-fields\n * - instance non-fields\n * - static fields\n * - instance fields\n *\n * @param {DecoratorInfo[]} info\n * @returns {DecoratorInfo[]} Sorted decoration info\n */\nfunction toSortedDecoratorInfo(info: DecoratorInfo[]): DecoratorInfo[] {\n return [\n ...info.filter(\n el => el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,\n ),\n ...info.filter(\n el => !el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,\n ),\n ...info.filter(el => el.isStatic && el.kind === FIELD),\n ...info.filter(el => !el.isStatic && el.kind === FIELD),\n ];\n}\n\ntype GenerateDecorationListResult = {\n // The zipped decorators array that will be passed to generateDecorationExprs\n decs: t.Expression[];\n // Whether there are non-empty decorator this values\n haveThis: boolean;\n};\n/**\n * Zip decorators and decorator this values into an array\n *\n * @param {t.Decorator[]} decorators\n * @param {((t.Expression | undefined)[])} decoratorsThis decorator this values\n * @param {DecoratorVersionKind} version\n * @returns {GenerateDecorationListResult}\n */\nfunction generateDecorationList(\n decorators: t.Decorator[],\n decoratorsThis: (t.Expression | undefined)[],\n version: DecoratorVersionKind,\n): GenerateDecorationListResult {\n const decsCount = decorators.length;\n const haveOneThis = decoratorsThis.some(Boolean);\n const decs: t.Expression[] = [];\n for (let i = 0; i < decsCount; i++) {\n if (\n (version === \"2023-11\" ||\n (!process.env.BABEL_8_BREAKING && version === \"2023-05\")) &&\n haveOneThis\n ) {\n decs.push(\n decoratorsThis[i] || t.unaryExpression(\"void\", t.numericLiteral(0)),\n );\n }\n decs.push(decorators[i].expression);\n }\n\n return { haveThis: haveOneThis, decs };\n}\n\nfunction generateDecorationExprs(\n decorationInfo: DecoratorInfo[],\n version: DecoratorVersionKind,\n): t.ArrayExpression {\n return t.arrayExpression(\n decorationInfo.map(el => {\n let flag = el.kind;\n if (el.isStatic) {\n flag +=\n version === \"2023-11\" ||\n (!process.env.BABEL_8_BREAKING && version === \"2023-05\")\n ? STATIC\n : STATIC_OLD_VERSION;\n }\n if (el.decoratorsHaveThis) flag += DECORATORS_HAVE_THIS;\n\n return t.arrayExpression([\n el.decoratorsArray,\n t.numericLiteral(flag),\n el.name,\n ...(el.privateMethods || []),\n ]);\n }),\n );\n}\n\nfunction extractElementLocalAssignments(decorationInfo: DecoratorInfo[]) {\n const localIds: t.Identifier[] = [];\n\n for (const el of decorationInfo) {\n const { locals } = el;\n\n if (Array.isArray(locals)) {\n localIds.push(...locals);\n } else if (locals !== undefined) {\n localIds.push(locals);\n }\n }\n\n return localIds;\n}\n\nfunction addCallAccessorsFor(\n version: DecoratorVersionKind,\n element: NodePath,\n key: t.PrivateName,\n getId: t.Identifier,\n setId: t.Identifier,\n isStatic: boolean,\n) {\n element.insertAfter(\n t.classPrivateMethod(\n \"get\",\n t.cloneNode(key),\n [],\n t.blockStatement([\n t.returnStatement(\n t.callExpression(\n t.cloneNode(getId),\n (process.env.BABEL_8_BREAKING || version === \"2023-11\") && isStatic\n ? []\n : [t.thisExpression()],\n ),\n ),\n ]),\n isStatic,\n ),\n );\n\n element.insertAfter(\n t.classPrivateMethod(\n \"set\",\n t.cloneNode(key),\n [t.identifier(\"v\")],\n t.blockStatement([\n t.expressionStatement(\n t.callExpression(\n t.cloneNode(setId),\n (process.env.BABEL_8_BREAKING || version === \"2023-11\") && isStatic\n ? [t.identifier(\"v\")]\n : [t.thisExpression(), t.identifier(\"v\")],\n ),\n ),\n ]),\n isStatic,\n ),\n );\n}\n\nfunction movePrivateAccessor(\n element: NodePath<t.ClassPrivateMethod>,\n key: t.PrivateName,\n methodLocalVar: t.Identifier,\n isStatic: boolean,\n) {\n let params: (t.Identifier | t.RestElement)[];\n let block: t.Statement[];\n\n if (element.node.kind === \"set\") {\n params = [t.identifier(\"v\")];\n block = [\n t.expressionStatement(\n t.callExpression(methodLocalVar, [\n t.thisExpression(),\n t.identifier(\"v\"),\n ]),\n ),\n ];\n } else {\n params = [];\n block = [\n t.returnStatement(t.callExpression(methodLocalVar, [t.thisExpression()])),\n ];\n }\n\n element.replaceWith(\n t.classPrivateMethod(\n element.node.kind,\n t.cloneNode(key),\n params,\n t.blockStatement(block),\n isStatic,\n ),\n );\n}\n\nfunction isClassDecoratableElementPath(\n path: NodePath<ClassElement>,\n): path is NodePath<ClassDecoratableElement> {\n const { type } = path;\n\n return (\n type !== \"TSDeclareMethod\" &&\n type !== \"TSIndexSignature\" &&\n type !== \"StaticBlock\"\n );\n}\n\nfunction staticBlockToIIFE(block: t.StaticBlock) {\n return t.callExpression(\n t.arrowFunctionExpression([], t.blockStatement(block.body)),\n [],\n );\n}\n\nfunction staticBlockToFunctionClosure(block: t.StaticBlock) {\n return t.functionExpression(null, [], t.blockStatement(block.body));\n}\n\nfunction fieldInitializerToClosure(value: t.Expression) {\n return t.functionExpression(\n null,\n [],\n t.blockStatement([t.returnStatement(value)]),\n );\n}\n\nfunction maybeSequenceExpression(exprs: t.Expression[]) {\n if (exprs.length === 0) return t.unaryExpression(\"void\", t.numericLiteral(0));\n if (exprs.length === 1) return exprs[0];\n return t.sequenceExpression(exprs);\n}\n\n/**\n * Create FunctionExpression from a ClassPrivateMethod.\n * The returned FunctionExpression node takes ownership of the private method's body and params.\n *\n * @param {t.ClassPrivateMethod} node\n * @returns\n */\nfunction createFunctionExpressionFromPrivateMethod(node: t.ClassPrivateMethod) {\n const { params, body, generator: isGenerator, async: isAsync } = node;\n return t.functionExpression(\n undefined,\n // @ts-expect-error todo: Improve typings: TSParameterProperty is only allowed in constructor\n params,\n body,\n isGenerator,\n isAsync,\n );\n}\n\nfunction createSetFunctionNameCall(\n state: PluginPass,\n className: t.Identifier | t.StringLiteral,\n) {\n return t.callExpression(state.addHelper(\"setFunctionName\"), [\n t.thisExpression(),\n className,\n ]);\n}\n\nfunction createToPropertyKeyCall(state: PluginPass, propertyKey: t.Expression) {\n return t.callExpression(state.addHelper(\"toPropertyKey\"), [propertyKey]);\n}\n\nfunction createPrivateBrandCheckClosure(brandName: t.PrivateName) {\n return t.arrowFunctionExpression(\n [t.identifier(\"_\")],\n t.binaryExpression(\"in\", t.cloneNode(brandName), t.identifier(\"_\")),\n );\n}\n\nfunction usesPrivateField(expression: t.Node) {\n try {\n t.traverseFast(expression, node => {\n if (t.isPrivateName(node)) {\n // TODO: Add early return support to t.traverseFast\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw null;\n }\n });\n return false;\n } catch {\n return true;\n }\n}\n\n/**\n * Convert a non-computed class element to its equivalent computed form.\n *\n * This function is to provide a decorator evaluation storage from non-computed\n * class elements.\n *\n * @param {(NodePath<t.ClassProperty | t.ClassMethod>)} path A non-computed class property or method\n */\nfunction convertToComputedKey(path: NodePath<t.ClassProperty | t.ClassMethod>) {\n const { node } = path;\n node.computed = true;\n if (t.isIdentifier(node.key)) {\n node.key = t.stringLiteral(node.key.name);\n }\n}\n\nfunction hasInstancePrivateAccess(path: NodePath, privateNames: string[]) {\n let containsInstancePrivateAccess = false;\n if (privateNames.length > 0) {\n const privateNameVisitor = privateNameVisitorFactory<\n PrivateNameVisitorState<null>,\n null\n >({\n PrivateName(path, state) {\n if (state.privateNamesMap.has(path.node.id.name)) {\n containsInstancePrivateAccess = true;\n path.stop();\n }\n },\n });\n const privateNamesMap = new Map<string, null>();\n for (const name of privateNames) {\n privateNamesMap.set(name, null);\n }\n path.traverse(privateNameVisitor, {\n privateNamesMap: privateNamesMap,\n });\n }\n return containsInstancePrivateAccess;\n}\n\nfunction checkPrivateMethodUpdateError(\n path: NodePath<t.Class>,\n decoratedPrivateMethods: Set<string>,\n) {\n const privateNameVisitor = privateNameVisitorFactory<\n PrivateNameVisitorState<null>,\n null\n >({\n PrivateName(path, state) {\n if (!state.privateNamesMap.has(path.node.id.name)) return;\n\n const parentPath = path.parentPath;\n const parentParentPath = parentPath.parentPath;\n\n if (\n // this.bar().#x = 123;\n (parentParentPath.node.type === \"AssignmentExpression\" &&\n parentParentPath.node.left === parentPath.node) ||\n // this.#x++;\n parentParentPath.node.type === \"UpdateExpression\" ||\n // ([...this.#x] = foo);\n parentParentPath.node.type === \"RestElement\" ||\n // ([this.#x] = foo);\n parentParentPath.node.type === \"ArrayPattern\" ||\n // ({ a: this.#x } = bar);\n (parentParentPath.node.type === \"ObjectProperty\" &&\n parentParentPath.node.value === parentPath.node &&\n parentParentPath.parentPath.type === \"ObjectPattern\") ||\n // for (this.#x of []);\n (parentParentPath.node.type === \"ForOfStatement\" &&\n parentParentPath.node.left === parentPath.node)\n ) {\n throw path.buildCodeFrameError(\n `Decorated private methods are read-only, but \"#${path.node.id.name}\" is updated via this expression.`,\n );\n }\n },\n });\n const privateNamesMap = new Map<string, null>();\n for (const name of decoratedPrivateMethods) {\n privateNamesMap.set(name, null);\n }\n path.traverse(privateNameVisitor, {\n privateNamesMap: privateNamesMap,\n });\n}\n\n/**\n * Apply decorator and accessor transform\n * @param path The class path.\n * @param state The plugin pass.\n * @param constantSuper The constantSuper compiler assumption.\n * @param ignoreFunctionLength The ignoreFunctionLength compiler assumption.\n * @param className The class name.\n * - If className is a `string`, it will be a valid identifier name that can safely serve as a class id\n * - If className is an Identifier, it is the reference to the name derived from NamedEvaluation\n * - If className is a StringLiteral, it is derived from NamedEvaluation on literal computed keys\n * @param propertyVisitor The visitor that should be applied on property prior to the transform.\n * @param version The decorator version.\n * @returns The transformed class path or undefined if there are no decorators.\n */\nfunction transformClass(\n path: NodePath<t.Class>,\n state: PluginPass,\n constantSuper: boolean,\n ignoreFunctionLength: boolean,\n className: string | t.Identifier | t.StringLiteral | undefined,\n propertyVisitor: Visitor<PluginPass>,\n version: DecoratorVersionKind,\n): NodePath | undefined {\n const body = path.get(\"body.body\");\n\n const classDecorators = path.node.decorators;\n let hasElementDecorators = false;\n let hasComputedKeysSideEffects = false;\n let elemDecsUseFnContext = false;\n\n const generateClassPrivateUid = createLazyPrivateUidGeneratorForClass(path);\n\n const classAssignments: t.AssignmentExpression[] = [];\n const scopeParent: Scope = path.scope.parent;\n const memoiseExpression = (\n expression: t.Expression,\n hint: string,\n assignments: t.AssignmentExpression[],\n ) => {\n const localEvaluatedId = generateLetUidIdentifier(scopeParent, hint);\n assignments.push(t.assignmentExpression(\"=\", localEvaluatedId, expression));\n return t.cloneNode(localEvaluatedId);\n };\n\n let protoInitLocal: t.Identifier;\n let staticInitLocal: t.Identifier;\n const classIdName = path.node.id?.name;\n // Whether to generate a setFunctionName call to preserve the class name\n const setClassName = typeof className === \"object\" ? className : undefined;\n // Check if the decorator does not reference function-specific\n // context or the given identifier name or contains yield or await expression.\n // `true` means \"maybe\" and `false` means \"no\".\n const usesFunctionContextOrYieldAwait = (decorator: t.Decorator) => {\n try {\n t.traverseFast(decorator, node => {\n if (\n t.isThisExpression(node) ||\n t.isSuper(node) ||\n t.isYieldExpression(node) ||\n t.isAwaitExpression(node) ||\n t.isIdentifier(node, { name: \"arguments\" }) ||\n (classIdName && t.isIdentifier(node, { name: classIdName })) ||\n (t.isMetaProperty(node) && node.meta.name !== \"import\")\n ) {\n // TODO: Add early return support to t.traverseFast\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw null;\n }\n });\n return false;\n } catch {\n return true;\n }\n };\n\n const instancePrivateNames: string[] = [];\n // Iterate over the class to see if we need to decorate it, and also to\n // transform simple auto accessors which are not decorated, and handle inferred\n // class name when the initializer of the class field is a class expression\n for (const element of body) {\n if (!isClassDecoratableElementPath(element)) {\n continue;\n }\n\n const elementNode = element.node;\n\n if (!elementNode.static && t.isPrivateName(elementNode.key)) {\n instancePrivateNames.push(elementNode.key.id.name);\n }\n\n if (isDecorated(elementNode)) {\n switch (elementNode.type) {\n case \"ClassProperty\":\n // @ts-expect-error todo: propertyVisitor.ClassProperty should be callable. Improve typings.\n propertyVisitor.ClassProperty(\n element as NodePath<t.ClassProperty>,\n state,\n );\n break;\n case \"ClassPrivateProperty\":\n // @ts-expect-error todo: propertyVisitor.ClassPrivateProperty should be callable. Improve typings.\n propertyVisitor.ClassPrivateProperty(\n element as NodePath<t.ClassPrivateProperty>,\n state,\n );\n break;\n case \"ClassAccessorProperty\":\n // @ts-expect-error todo: propertyVisitor.ClassAccessorProperty should be callable. Improve typings.\n propertyVisitor.ClassAccessorProperty(\n element as NodePath<t.ClassAccessorProperty>,\n state,\n );\n if (version === \"2023-11\") {\n break;\n }\n /* fallthrough */\n default:\n if (elementNode.static) {\n staticInitLocal ??= generateLetUidIdentifier(\n scopeParent,\n \"initStatic\",\n );\n } else {\n protoInitLocal ??= generateLetUidIdentifier(\n scopeParent,\n \"initProto\",\n );\n }\n break;\n }\n hasElementDecorators = true;\n elemDecsUseFnContext ||= elementNode.decorators.some(\n usesFunctionContextOrYieldAwait,\n );\n } else if (elementNode.type === \"ClassAccessorProperty\") {\n // @ts-expect-error todo: propertyVisitor.ClassAccessorProperty should be callable. Improve typings.\n propertyVisitor.ClassAccessorProperty(\n element as NodePath<t.ClassAccessorProperty>,\n state,\n );\n const { key, value, static: isStatic, computed } = elementNode;\n\n const newId = generateClassPrivateUid();\n const newField = generateClassProperty(newId, value, isStatic);\n const keyPath = element.get(\"key\");\n const [newPath] = element.replaceWith(newField);\n\n let getterKey, setterKey;\n if (computed && !keyPath.isConstantExpression()) {\n getterKey = memoiseComputedKey(\n createToPropertyKeyCall(state, key as t.Expression),\n scopeParent,\n scopeParent.generateUid(\"computedKey\"),\n )!;\n setterKey = t.cloneNode(getterKey.left as t.Identifier);\n } else {\n getterKey = t.cloneNode(key);\n setterKey = t.cloneNode(key);\n }\n\n assignIdForAnonymousClass(path, className);\n\n addProxyAccessorsFor(\n path.node.id,\n newPath,\n getterKey,\n setterKey,\n newId,\n computed,\n isStatic,\n version,\n );\n }\n\n if (\"computed\" in element.node && element.node.computed) {\n hasComputedKeysSideEffects ||= !scopeParent.isStatic(element.node.key);\n }\n }\n\n if (!classDecorators && !hasElementDecorators) {\n if (!path.node.id && typeof className === \"string\") {\n path.node.id = t.identifier(className);\n }\n if (setClassName) {\n path.node.body.body.unshift(\n createStaticBlockFromExpressions([\n createSetFunctionNameCall(state, setClassName),\n ]),\n );\n }\n // If nothing is decorated and no assignments inserted, return\n return;\n }\n\n const elementDecoratorInfo: DecoratorInfo[] = [];\n\n let constructorPath: NodePath<t.ClassMethod> | undefined;\n const decoratedPrivateMethods = new Set<string>();\n\n let classInitLocal: t.Identifier, classIdLocal: t.Identifier;\n let decoratorReceiverId: t.Identifier | null = null;\n\n // Memoise the this value `a.b` of decorator member expressions `@a.b.dec`,\n type HandleDecoratorsResult = {\n // whether the whole decorator list requires memoisation\n hasSideEffects: boolean;\n usesFnContext: boolean;\n // the this value of each decorator if applicable\n decoratorsThis: (t.Expression | undefined)[];\n };\n function handleDecorators(decorators: t.Decorator[]): HandleDecoratorsResult {\n let hasSideEffects = false;\n let usesFnContext = false;\n const decoratorsThis: (t.Expression | null)[] = [];\n for (const decorator of decorators) {\n const { expression } = decorator;\n let object;\n if (\n (version === \"2023-11\" ||\n (!process.env.BABEL_8_BREAKING && version === \"2023-05\")) &&\n t.isMemberExpression(expression)\n ) {\n if (t.isSuper(expression.object)) {\n object = t.thisExpression();\n } else if (scopeParent.isStatic(expression.object)) {\n object = t.cloneNode(expression.object);\n } else {\n decoratorReceiverId ??= generateLetUidIdentifier(scopeParent, \"obj\");\n object = t.assignmentExpression(\n \"=\",\n t.cloneNode(decoratorReceiverId),\n expression.object,\n );\n expression.object = t.cloneNode(decoratorReceiverId);\n }\n }\n decoratorsThis.push(object);\n hasSideEffects ||= !scopeParent.isStatic(expression);\n usesFnContext ||= usesFunctionContextOrYieldAwait(decorator);\n }\n return { hasSideEffects, usesFnContext, decoratorsThis };\n }\n\n const willExtractSomeElemDecs =\n hasComputedKeysSideEffects ||\n (process.env.BABEL_8_BREAKING\n ? elemDecsUseFnContext\n : elemDecsUseFnContext || version !== \"2023-11\");\n\n let needsDeclaraionForClassBinding = false;\n let classDecorationsFlag = 0;\n let classDecorations: t.Expression[] = [];\n let classDecorationsId: t.Identifier;\n let computedKeyAssignments: t.AssignmentExpression[] = [];\n if (classDecorators) {\n classInitLocal = generateLetUidIdentifier(scopeParent, \"initClass\");\n needsDeclaraionForClassBinding = path.isClassDeclaration();\n ({ id: classIdLocal, path } = replaceClassWithVar(path, className));\n\n path.node.decorators = null;\n\n const classDecsUsePrivateName = classDecorators.some(usesPrivateField);\n const { hasSideEffects, usesFnContext, decoratorsThis } =\n handleDecorators(classDecorators);\n\n const { haveThis, decs } = generateDecorationList(\n classDecorators,\n decoratorsThis,\n version,\n );\n classDecorationsFlag = haveThis ? 1 : 0;\n classDecorations = decs;\n\n if (\n usesFnContext ||\n (hasSideEffects && willExtractSomeElemDecs) ||\n classDecsUsePrivateName\n ) {\n classDecorationsId = memoiseExpression(\n t.arrayExpression(classDecorations),\n \"classDecs\",\n classAssignments,\n );\n }\n\n if (!hasElementDecorators) {\n // Sync body paths as non-decorated computed accessors have been transpiled\n // to getter-setter pairs.\n for (const element of path.get(\"body.body\")) {\n const { node } = element;\n const isComputed = \"computed\" in node && node.computed;\n if (isComputed) {\n if (element.isClassProperty({ static: true })) {\n if (!element.get(\"key\").isConstantExpression()) {\n const key = (node as t.ClassProperty).key;\n const maybeAssignment = memoiseComputedKey(\n key,\n scopeParent,\n scopeParent.generateUid(\"computedKey\"),\n );\n if (maybeAssignment != null) {\n // If it is a static computed field within a decorated class, we move the computed key\n // into `computedKeyAssignments` which will be then moved into the non-static class,\n // to ensure that the evaluation order and private environment are correct\n node.key = t.cloneNode(maybeAssignment.left);\n computedKeyAssignments.push(maybeAssignment);\n }\n }\n } else if (computedKeyAssignments.length > 0) {\n prependExpressionsToComputedKey(\n computedKeyAssignments,\n element as NodePath<ClassElementCanHaveComputedKeys>,\n );\n computedKeyAssignments = [];\n }\n }\n }\n }\n } else {\n assignIdForAnonymousClass(path, className);\n classIdLocal = t.cloneNode(path.node.id);\n }\n\n let lastInstancePrivateName: t.PrivateName;\n let needsInstancePrivateBrandCheck = false;\n\n let fieldInitializerExpressions = [];\n let staticFieldInitializerExpressions: t.Expression[] = [];\n\n if (hasElementDecorators) {\n if (protoInitLocal) {\n const protoInitCall = t.callExpression(t.cloneNode(protoInitLocal), [\n t.thisExpression(),\n ]);\n fieldInitializerExpressions.push(protoInitCall);\n }\n for (const element of body) {\n if (!isClassDecoratableElementPath(element)) {\n if (\n staticFieldInitializerExpressions.length > 0 &&\n element.isStaticBlock()\n ) {\n prependExpressionsToStaticBlock(\n staticFieldInitializerExpressions,\n element,\n );\n staticFieldInitializerExpressions = [];\n }\n continue;\n }\n\n const { node } = element;\n const decorators = node.decorators;\n\n const hasDecorators = !!decorators?.len