391 lines
14 KiB
Go
391 lines
14 KiB
Go
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
|
|
}
|