package tstransforms import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/transformers" ) const USE_NEW_TYPE_METADATA_FORMAT = false type MetadataTransformer struct { transformers.Transformer legacyDecorators bool resolver printer.EmitResolver serializer *metadataSerializer languageVersion core.ScriptTarget strictNullChecks bool parent *ast.Node currentLexicalScope *ast.Node } func NewMetadataTransformer(opt *transformers.TransformOptions) *transformers.Transformer { tx := &MetadataTransformer{ legacyDecorators: opt.CompilerOptions.ExperimentalDecorators.IsTrue(), resolver: opt.EmitResolver, languageVersion: opt.CompilerOptions.GetEmitScriptTarget(), strictNullChecks: opt.CompilerOptions.GetStrictOptionValue(opt.CompilerOptions.StrictNullChecks), } return tx.NewTransformer(tx.visit, opt.Context) } func (tx *MetadataTransformer) visit(node *ast.Node) *ast.Node { if (node.SubtreeFacts() & ast.SubtreeContainsDecorators) == 0 { return node } switch node.Kind { case ast.KindClassDeclaration: return tx.visitClassDeclaration(node.AsClassDeclaration()) case ast.KindClassExpression: return tx.visitClassExpression(node.AsClassExpression()) case ast.KindPropertyDeclaration: return tx.visitPropertyDeclaration(node.AsPropertyDeclaration()) case ast.KindMethodDeclaration: return tx.visitMethodDeclaration(node.AsMethodDeclaration()) case ast.KindSetAccessor: return tx.visitSetAccessor(node.AsSetAccessorDeclaration()) case ast.KindGetAccessor: return tx.visitGetAccessor(node.AsGetAccessorDeclaration()) case ast.KindSourceFile: tx.parent = nil defer tx.setParent(nil) tx.currentLexicalScope = node defer tx.setCurrentLexicalScope(nil) tx.serializer = newMetadataSerializer(tx.resolver, tx.Factory(), tx.EmitContext(), tx.languageVersion, tx.strictNullChecks) updated := tx.Visitor().VisitEachChild(node) tx.EmitContext().AddEmitHelper(updated, tx.EmitContext().ReadEmitHelpers()...) return updated case ast.KindModuleBlock, ast.KindBlock, ast.KindCaseBlock: oldScope := tx.currentLexicalScope tx.currentLexicalScope = node defer tx.setCurrentLexicalScope(oldScope) return tx.Visitor().VisitEachChild(node) default: return tx.Visitor().VisitEachChild(node) } } func (tx *MetadataTransformer) setParent(node *ast.Node) { tx.parent = node } func (tx *MetadataTransformer) setCurrentLexicalScope(node *ast.Node) { tx.currentLexicalScope = node } func (tx *MetadataTransformer) visitClassExpression(node *ast.ClassExpression) *ast.Node { oldParent := tx.parent tx.parent = node.AsNode() defer tx.setParent(oldParent) if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode()) return tx.Factory().UpdateClassExpression( node, modifiers, tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNodes(node.TypeParameters), tx.Visitor().VisitNodes(node.HeritageClauses), tx.Visitor().VisitNodes(node.Members), ) } func (tx *MetadataTransformer) visitClassDeclaration(node *ast.ClassDeclaration) *ast.Node { oldParent := tx.parent tx.parent = node.AsNode() defer tx.setParent(oldParent) if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode()) return tx.Factory().UpdateClassDeclaration( node, modifiers, tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNodes(node.TypeParameters), tx.Visitor().VisitNodes(node.HeritageClauses), tx.Visitor().VisitNodes(node.Members), ) } func (tx *MetadataTransformer) visitPropertyDeclaration(node *ast.PropertyDeclaration) *ast.Node { if !ast.HasDecorators(node.AsNode()) { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent) return tx.Factory().UpdatePropertyDeclaration( node, modifiers, tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNode(node.PostfixToken), tx.Visitor().VisitNode(node.Type), tx.Visitor().VisitNode(node.Initializer), ) } func (tx *MetadataTransformer) visitMethodDeclaration(node *ast.MethodDeclaration) *ast.Node { if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent) return tx.Factory().UpdateMethodDeclaration( node, modifiers, tx.Visitor().VisitNode(node.AsteriskToken), tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNode(node.PostfixToken), tx.Visitor().VisitNodes(node.TypeParameters), tx.Visitor().VisitNodes(node.Parameters), tx.Visitor().VisitNode(node.Type), tx.Visitor().VisitNode(node.FullSignature), tx.Visitor().VisitNode(node.Body), ) } func (tx *MetadataTransformer) visitSetAccessor(node *ast.SetAccessorDeclaration) *ast.Node { if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent) return tx.Factory().UpdateSetAccessorDeclaration( node, modifiers, tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNodes(node.TypeParameters), tx.Visitor().VisitNodes(node.Parameters), tx.Visitor().VisitNode(node.Type), tx.Visitor().VisitNode(node.FullSignature), tx.Visitor().VisitNode(node.Body), ) } func (tx *MetadataTransformer) visitGetAccessor(node *ast.GetAccessorDeclaration) *ast.Node { if !ast.HasDecorators(node.AsNode()) { return tx.Visitor().VisitEachChild(node.AsNode()) } modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent) return tx.Factory().UpdateGetAccessorDeclaration( node, modifiers, tx.Visitor().VisitNode(node.Name()), tx.Visitor().VisitNodes(node.TypeParameters), tx.Visitor().VisitNodes(node.Parameters), tx.Visitor().VisitNode(node.Type), tx.Visitor().VisitNode(node.FullSignature), tx.Visitor().VisitNode(node.Body), ) } func (tx *MetadataTransformer) injectClassTypeMetadata(list *ast.ModifierList, node *ast.Node) *ast.ModifierList { metadata := tx.getTypeMetadata(node, node) if len(metadata) > 0 { var originalNodes []*ast.Node if list != nil { originalNodes = list.Nodes } if len(originalNodes) == 0 { res := tx.Factory().NewModifierList(metadata) if list != nil { res.Loc = list.Loc } return res } var modifiersArray []*ast.Node if ast.IsModifier(originalNodes[0]) && (originalNodes[0].Kind == ast.KindDefaultKeyword || originalNodes[0].Kind == ast.KindExportKeyword) { modifiersArray = append(modifiersArray, originalNodes[0]) if len(originalNodes) > 1 && (originalNodes[1].Kind == ast.KindDefaultKeyword || originalNodes[1].Kind == ast.KindExportKeyword) { modifiersArray = append(modifiersArray, originalNodes[1]) } } restStart := len(modifiersArray) decos := core.Filter(originalNodes, ast.IsDecorator) modifiersArray = append(modifiersArray, decos...) modifiersArray = append(modifiersArray, metadata...) otherModifiers := core.Filter(originalNodes[restStart:], ast.IsModifier) modifiersArray = append(modifiersArray, otherModifiers...) res := tx.Factory().NewModifierList(modifiersArray) res.Loc = list.Loc return res } return list } func (tx *MetadataTransformer) injectClassElementTypeMetadata(list *ast.ModifierList, node *ast.Node, container *ast.Node) *ast.ModifierList { if !ast.IsClassLike(container) { return list } if !ast.ClassElementOrClassElementParameterIsDecorated(tx.legacyDecorators, node, container) { return list } metadata := tx.getTypeMetadata(node, container) if len(metadata) > 0 { var originalNodes []*ast.Node if list != nil { originalNodes = list.Nodes } if len(originalNodes) == 0 { res := tx.Factory().NewModifierList(metadata) if list != nil { res.Loc = list.Loc } return res } var modifiersArray []*ast.Node decos := core.Filter(originalNodes, ast.IsDecorator) modifiersArray = append(modifiersArray, decos...) modifiersArray = append(modifiersArray, metadata...) modifiers := core.Filter(originalNodes, ast.IsModifier) modifiersArray = append(modifiersArray, modifiers...) res := tx.Factory().NewModifierList(modifiersArray) res.Loc = list.Loc return res } return list } /** * Gets optional type metadata for a declaration. * * @param node The declaration node. */ func (tx *MetadataTransformer) getTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node { // Decorator metadata is not yet supported for ES decorators. if !tx.legacyDecorators { return nil } if USE_NEW_TYPE_METADATA_FORMAT { return tx.getNewTypeMetadata(node, container) } return tx.getOldTypeMetadata(node, container) } func (tx *MetadataTransformer) getOldTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node { var decorators []*ast.Node if tx.shouldAddTypeMetadata(node) { typeMetadata := tx.Factory().NewMetadataHelper("design:type", tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container)) decorators = append(decorators, tx.Factory().NewDecorator(typeMetadata)) } if tx.shouldAddParamTypesMetadata(node) { paramTypesMetadata := tx.Factory().NewMetadataHelper("design:paramtypes", tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container)) decorators = append(decorators, tx.Factory().NewDecorator(paramTypesMetadata)) } if tx.shouldAddReturnTypeMetadata(node) { returnTypeMetadata := tx.Factory().NewMetadataHelper("design:returntype", tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node)) decorators = append(decorators, tx.Factory().NewDecorator(returnTypeMetadata)) } return decorators } func (tx *MetadataTransformer) getNewTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node { var properties []*ast.Node if tx.shouldAddTypeMetadata(node) { properties = append(properties, tx.Factory().NewPropertyAssignment( nil, tx.Factory().NewIdentifier("type"), nil, nil, tx.Factory().NewArrowFunction( nil, nil, tx.Factory().NewNodeList([]*ast.Node{}), nil, nil, tx.Factory().NewToken(ast.KindEqualsGreaterThanToken), tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container), ), )) } if tx.shouldAddParamTypesMetadata(node) { properties = append(properties, tx.Factory().NewPropertyAssignment( nil, tx.Factory().NewIdentifier("paramTypes"), nil, nil, tx.Factory().NewArrowFunction( nil, nil, tx.Factory().NewNodeList([]*ast.Node{}), nil, nil, tx.Factory().NewToken(ast.KindEqualsGreaterThanToken), tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container), ), )) } if tx.shouldAddReturnTypeMetadata(node) { properties = append(properties, tx.Factory().NewPropertyAssignment( nil, tx.Factory().NewIdentifier("returnType"), nil, nil, tx.Factory().NewArrowFunction( nil, nil, tx.Factory().NewNodeList([]*ast.Node{}), nil, nil, tx.Factory().NewToken(ast.KindEqualsGreaterThanToken), tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node), ), )) } if len(properties) > 0 { typeInfoMetadata := tx.Factory().NewMetadataHelper("design:typeinfo", tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(properties), true)) return []*ast.Node{tx.Factory().NewDecorator(typeInfoMetadata)} } return nil } /** * Determines whether to emit the "design:type" metadata based on the node's kind. * The caller should have already tested whether the node has decorators and whether the * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ func (tx *MetadataTransformer) shouldAddTypeMetadata(node *ast.Node) bool { switch node.Kind { case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration: return true } return false } /** * Determines whether to emit the "design:returntype" metadata based on the node's kind. * The caller should have already tested whether the node has decorators and whether the * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ func (tx *MetadataTransformer) shouldAddReturnTypeMetadata(node *ast.Node) bool { return node.Kind == ast.KindMethodDeclaration } /** * Determines whether to emit the "design:paramtypes" metadata based on the node's kind. * The caller should have already tested whether the node has decorators and whether the * emitDecoratorMetadata compiler option is set. * * @param node The node to test. */ func (tx *MetadataTransformer) shouldAddParamTypesMetadata(node *ast.Node) bool { switch node.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: return ast.GetFirstConstructorWithBody(node) != nil case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: return true } return false }