2025-10-15 10:12:44 +03:00

1981 lines
74 KiB
Go

package declarations
import (
"fmt"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
type ReferencedFilePair struct {
file *ast.SourceFile
ref *ast.FileReference
}
type OutputPaths interface {
DeclarationFilePath() string
JsFilePath() string
}
// Used to be passed in the TransformationContext, which is now just an EmitContext
type DeclarationEmitHost interface {
modulespecifiers.ModuleSpecifierGenerationHost
GetCurrentDirectory() string
UseCaseSensitiveFileNames() bool
GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile
GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths
GetResolutionModeOverride(node *ast.Node) core.ResolutionMode
GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags
GetEmitResolver() printer.EmitResolver
}
type DeclarationTransformer struct {
transformers.Transformer
host DeclarationEmitHost
compilerOptions *core.CompilerOptions
tracker *SymbolTrackerImpl
state *SymbolTrackerSharedState
resolver printer.EmitResolver
declarationFilePath string
declarationMapPath string
isBundledEmit bool
needsDeclare bool
needsScopeFixMarker bool
resultHasScopeMarker bool
enclosingDeclaration *ast.Node
resultHasExternalModuleIndicator bool
suppressNewDiagnosticContexts bool
lateStatementReplacementMap map[ast.NodeId]*ast.Node
expandoHosts collections.Set[ast.NodeId]
rawReferencedFiles []ReferencedFilePair
rawTypeReferenceDirectives []*ast.FileReference
rawLibReferenceDirectives []*ast.FileReference
}
// TODO: Convert to transformers.TransformerFactory signature to allow more automatic composition with other transforms
func NewDeclarationTransformer(host DeclarationEmitHost, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer {
resolver := host.GetEmitResolver()
state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver}
tracker := NewSymbolTracker(host, resolver, state)
// TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit)
tx := &DeclarationTransformer{
host: host,
compilerOptions: compilerOptions,
tracker: tracker,
state: state,
resolver: resolver,
declarationFilePath: declarationFilePath,
declarationMapPath: declarationMapPath,
}
tx.NewTransformer(tx.visit, context)
return tx
}
func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic {
return tx.state.diagnostics
}
const declarationEmitNodeBuilderFlags = nodebuilder.FlagsMultilineObjectLiterals |
nodebuilder.FlagsWriteClassExpressionAsTypeLiteral |
nodebuilder.FlagsUseTypeOfFunction |
nodebuilder.FlagsUseStructuralFallback |
nodebuilder.FlagsAllowEmptyTuple |
nodebuilder.FlagsGenerateNamesForShadowedTypeParams |
nodebuilder.FlagsNoTruncation
const declarationEmitInternalNodeBuilderFlags = nodebuilder.InternalFlagsAllowUnresolvedNames
// functions as both `visitDeclarationStatements` and `transformRoot`, utilitzing SyntaxList nodes
func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node {
if node == nil {
return nil
}
// !!! TODO: Bundle support?
switch node.Kind {
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
// statements we keep but do something to
case ast.KindFunctionDeclaration,
ast.KindModuleDeclaration,
ast.KindImportEqualsDeclaration,
ast.KindInterfaceDeclaration,
ast.KindClassDeclaration,
ast.KindJSTypeAliasDeclaration,
ast.KindTypeAliasDeclaration,
ast.KindEnumDeclaration,
ast.KindVariableStatement,
ast.KindImportDeclaration,
ast.KindJSImportDeclaration,
ast.KindExportDeclaration,
ast.KindJSExportAssignment,
ast.KindExportAssignment:
return tx.visitDeclarationStatements(node)
// statements we elide
case ast.KindBreakStatement,
ast.KindContinueStatement,
ast.KindDebuggerStatement,
ast.KindDoStatement,
ast.KindEmptyStatement,
ast.KindForInStatement,
ast.KindForOfStatement,
ast.KindForStatement,
ast.KindIfStatement,
ast.KindLabeledStatement,
ast.KindReturnStatement,
ast.KindSwitchStatement,
ast.KindThrowStatement,
ast.KindTryStatement,
ast.KindWhileStatement,
ast.KindWithStatement,
ast.KindNotEmittedStatement,
ast.KindBlock,
ast.KindMissingDeclaration:
return nil
case ast.KindExpressionStatement:
return tx.visitExpressionStatement(node.AsExpressionStatement())
// parts of things, things we just visit children of
default:
return tx.visitDeclarationSubtree(node)
}
}
func throwDiagnostic(result printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
panic("Diagnostic emitted without context")
}
func (tx *DeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
tx.isBundledEmit = false
tx.needsDeclare = true
tx.needsScopeFixMarker = false
tx.resultHasScopeMarker = false
tx.enclosingDeclaration = node.AsNode()
tx.state.getSymbolAccessibilityDiagnostic = throwDiagnostic
tx.resultHasExternalModuleIndicator = false
tx.suppressNewDiagnosticContexts = false
tx.state.lateMarkedStatements = make([]*ast.Node, 0)
tx.lateStatementReplacementMap = make(map[ast.NodeId]*ast.Node)
tx.expandoHosts = collections.Set[ast.NodeId]{}
tx.rawReferencedFiles = make([]ReferencedFilePair, 0)
tx.rawTypeReferenceDirectives = make([]*ast.FileReference, 0)
tx.rawLibReferenceDirectives = make([]*ast.FileReference, 0)
tx.state.currentSourceFile = node
tx.collectFileReferences(node)
tx.resolver.PrecalculateDeclarationEmitVisibility(node)
updated := tx.transformSourceFile(node)
tx.state.currentSourceFile = nil
return updated
}
func (tx *DeclarationTransformer) collectFileReferences(sourceFile *ast.SourceFile) {
tx.rawReferencedFiles = append(tx.rawReferencedFiles, core.Map(sourceFile.ReferencedFiles, func(ref *ast.FileReference) ReferencedFilePair { return ReferencedFilePair{file: sourceFile, ref: ref} })...)
tx.rawTypeReferenceDirectives = append(tx.rawTypeReferenceDirectives, sourceFile.TypeReferenceDirectives...)
tx.rawLibReferenceDirectives = append(tx.rawLibReferenceDirectives, sourceFile.LibReferenceDirectives...)
}
func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast.Node {
var combinedStatements *ast.StatementList
statements := tx.Visitor().VisitNodes(node.Statements)
combinedStatements = tx.transformAndReplaceLatePaintedStatements(statements)
combinedStatements.Loc = statements.Loc // setTextRange
if ast.IsExternalOrCommonJSModule(node) && (!tx.resultHasExternalModuleIndicator || (tx.needsScopeFixMarker && !tx.resultHasScopeMarker)) {
marker := createEmptyExports(tx.Factory().AsNodeFactory())
newList := append(combinedStatements.Nodes, marker)
withMarker := tx.Factory().NewNodeList(newList)
withMarker.Loc = combinedStatements.Loc
combinedStatements = withMarker
}
outputFilePath := tspath.GetDirectoryPath(tspath.NormalizeSlashes(tx.declarationFilePath))
result := tx.Factory().UpdateSourceFile(node, combinedStatements, node.EndOfFileToken)
result.AsSourceFile().LibReferenceDirectives = tx.getLibReferences()
result.AsSourceFile().TypeReferenceDirectives = tx.getTypeReferences()
result.AsSourceFile().IsDeclarationFile = true
result.AsSourceFile().ReferencedFiles = tx.getReferencedFiles(outputFilePath)
return result.AsNode()
}
func createEmptyExports(factory *ast.NodeFactory) *ast.Node {
return factory.NewExportDeclaration(nil /*isTypeOnly*/, false, factory.NewNamedExports(factory.NewNodeList([]*ast.Node{})), nil, nil)
}
func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(statements *ast.StatementList) *ast.StatementList {
// This is a `while` loop because `handleSymbolAccessibilityError` can see additional import aliases marked as visible during
// error handling which must now be included in the output and themselves checked for errors.
// For example:
// ```
// module A {
// export module Q {}
// import B = Q;
// import C = B;
// export import D = C;
// }
// ```
// In such a scenario, only Q and D are initially visible, but we don't consider imports as private names - instead we say they if they are referenced they must
// be recorded. So while checking D's visibility we mark C as visible, then we must check C which in turn marks B, completing the chain of
// dependent imports and allowing a valid declaration file output. Today, this dependent alias marking only happens for internal import aliases.
for true {
if len(tx.state.lateMarkedStatements) == 0 {
break
}
next := tx.state.lateMarkedStatements[0]
tx.state.lateMarkedStatements = tx.state.lateMarkedStatements[1:]
saveNeedsDeclare := tx.needsDeclare
tx.needsDeclare = next.Parent != nil && ast.IsSourceFile(next.Parent) && !(ast.IsExternalModule(next.Parent.AsSourceFile()) && tx.isBundledEmit)
result := tx.transformTopLevelDeclaration(next)
tx.needsDeclare = saveNeedsDeclare
original := tx.EmitContext().MostOriginal(next)
id := ast.GetNodeId(original)
tx.lateStatementReplacementMap[id] = result
}
// And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list
// (and remove them from the set to examine for outter declarations)
results := make([]*ast.Node, 0, len(statements.Nodes))
for _, statement := range statements.Nodes {
if !ast.IsLateVisibilityPaintedStatement(statement) {
results = append(results, statement)
continue
}
original := tx.EmitContext().MostOriginal(statement)
id := ast.GetNodeId(original)
replacement, ok := tx.lateStatementReplacementMap[id]
if !ok {
results = append(results, statement)
continue // not replaced
}
if replacement == nil {
continue // deleted
}
if replacement.Kind == ast.KindSyntaxList {
if !tx.needsScopeFixMarker || !tx.resultHasExternalModuleIndicator {
for _, elem := range replacement.AsSyntaxList().Children {
if needsScopeMarker(elem) {
tx.needsScopeFixMarker = true
}
if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) {
tx.resultHasExternalModuleIndicator = true
}
}
}
results = append(results, replacement.AsSyntaxList().Children...)
} else {
if needsScopeMarker(replacement) {
tx.needsScopeFixMarker = true
}
if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) {
tx.resultHasExternalModuleIndicator = true
}
results = append(results, replacement)
}
}
return tx.Factory().NewNodeList(results)
}
func (tx *DeclarationTransformer) getReferencedFiles(outputFilePath string) (results []*ast.FileReference) {
// Handle path rewrites for triple slash ref comments
for _, pair := range tx.rawReferencedFiles {
sourceFile := pair.file
ref := pair.ref
if !ref.Preserve {
continue
}
file := tx.host.GetSourceFileFromReference(sourceFile, ref)
if file == nil {
continue
}
var declFileName string
if file.IsDeclarationFile {
declFileName = file.FileName()
} else {
// !!! bundled emit support, omit bundled refs
// if (tx.isBundledEmit && contains((node as Bundle).sourceFiles, file)) continue
paths := tx.host.GetOutputPathsFor(file, true)
// Try to use output path for referenced file, or output js path if that doesn't exist, or the input path if all else fails
declFileName = paths.DeclarationFilePath()
if len(declFileName) == 0 {
declFileName = paths.JsFilePath()
}
if len(declFileName) == 0 {
declFileName = file.FileName()
}
}
// Should only be missing if the source file is missing a fileName (at which point we can't name a reference to it anyway)
// TODO: Shouldn't this be a crash or assert instead of a silent continue?
if len(declFileName) == 0 {
continue
}
fileName := tspath.GetRelativePathToDirectoryOrUrl(
outputFilePath,
declFileName,
false, // TODO: Probably unsafe to assume this isn't a URL, but that's what strada does
tspath.ComparePathsOptions{
CurrentDirectory: tx.host.GetCurrentDirectory(),
UseCaseSensitiveFileNames: tx.host.UseCaseSensitiveFileNames(),
},
)
results = append(results, &ast.FileReference{
TextRange: core.NewTextRange(-1, -1),
FileName: fileName,
ResolutionMode: ref.ResolutionMode,
Preserve: ref.Preserve,
})
}
return results
}
func (tx *DeclarationTransformer) getLibReferences() (result []*ast.FileReference) {
// clone retained references
for _, ref := range tx.rawLibReferenceDirectives {
if !ref.Preserve {
continue
}
result = append(result, &ast.FileReference{
TextRange: core.NewTextRange(-1, -1),
FileName: ref.FileName,
ResolutionMode: ref.ResolutionMode,
Preserve: ref.Preserve,
})
}
return result
}
func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReference) {
// clone retained references
for _, ref := range tx.rawTypeReferenceDirectives {
if !ref.Preserve {
continue
}
result = append(result, &ast.FileReference{
TextRange: core.NewTextRange(-1, -1),
FileName: ref.FileName,
ResolutionMode: ref.ResolutionMode,
Preserve: ref.Preserve,
})
}
return result
}
func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node {
// !!! TODO: stripInternal support?
// if (shouldStripInternal(input)) return nil
if ast.IsDeclaration(input) {
if isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) {
return nil
}
if ast.HasDynamicName(input) {
if tx.state.isolatedDeclarations {
// Classes and object literals usually elide properties with computed names that are not of a literal type
// In isolated declarations TSC needs to error on these as we don't know the type in a DTE.
if !tx.resolver.IsDefinitelyReferenceToGlobalSymbolObject(input.Name().Expression()) {
if ast.IsClassDeclaration(input.Parent) || ast.IsObjectLiteralExpression(input.Parent) {
// !!! TODO: isolatedDeclarations diagnostics
// context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_property_names_on_class_or_object_literals_cannot_be_inferred_with_isolatedDeclarations))
return nil
} else if (ast.IsInterfaceDeclaration(input.Parent) || ast.IsTypeLiteralNode(input.Parent)) && !ast.IsEntityNameExpression(input.Name().Expression()) {
// Type declarations just need to double-check that the input computed name is an entity name expression
// !!! TODO: isolatedDeclarations diagnostics
// context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_properties_must_be_number_or_string_literals_variables_or_dotted_expressions_with_isolatedDeclarations))
return nil
}
}
} else if !tx.resolver.IsLateBound(tx.EmitContext().ParseNode(input)) || !ast.IsEntityNameExpression(input.Name().AsComputedPropertyName().Expression) {
return nil
}
}
}
// Elide implementation signatures from overload sets
if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) {
return nil
}
if input.Kind == ast.KindSemicolonClassElement {
return nil
}
previousEnclosingDeclaration := tx.enclosingDeclaration
if isEnclosingDeclaration(input) {
tx.enclosingDeclaration = input
}
canProdiceDiagnostic := canProduceDiagnostics(input)
oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts
shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && !(input.Parent.Kind == ast.KindTypeAliasDeclaration || input.Parent.Kind == ast.KindJSTypeAliasDeclaration)
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
if canProdiceDiagnostic && !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input)
}
oldName := tx.state.errorNameNode
if shouldEnterSuppressNewDiagnosticsContextContext {
tx.suppressNewDiagnosticContexts = true
}
var result *ast.Node
switch input.Kind {
case ast.KindMappedType:
result = tx.transformMappedTypeNode(input.AsMappedTypeNode())
case ast.KindHeritageClause:
result = tx.transformHeritageClause(input.AsHeritageClause())
case ast.KindMethodSignature:
result = tx.transformMethodSignatureDeclaration(input.AsMethodSignatureDeclaration())
case ast.KindMethodDeclaration:
result = tx.transformMethodDeclaration(input.AsMethodDeclaration())
case ast.KindConstructSignature:
result = tx.transformConstructSignatureDeclaration(input.AsConstructSignatureDeclaration())
case ast.KindConstructor:
result = tx.transformConstructorDeclaration(input.AsConstructorDeclaration())
case ast.KindGetAccessor:
result = tx.transformGetAccesorDeclaration(input.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
result = tx.transformSetAccessorDeclaration(input.AsSetAccessorDeclaration())
case ast.KindPropertyDeclaration:
result = tx.transformPropertyDeclaration(input.AsPropertyDeclaration())
case ast.KindPropertySignature:
result = tx.transformPropertySignatureDeclaration(input.AsPropertySignatureDeclaration())
case ast.KindCallSignature:
result = tx.transformCallSignatureDeclaration(input.AsCallSignatureDeclaration())
case ast.KindIndexSignature:
result = tx.transformIndexSignatureDeclaration(input.AsIndexSignatureDeclaration())
case ast.KindVariableDeclaration:
result = tx.transformVariableDeclaration(input.AsVariableDeclaration())
case ast.KindTypeParameter:
result = tx.transformTypeParameterDeclaration(input.AsTypeParameter())
case ast.KindExpressionWithTypeArguments:
result = tx.transformExpressionWithTypeArguments(input.AsExpressionWithTypeArguments())
case ast.KindTypeReference:
result = tx.transformTypeReference(input.AsTypeReference())
case ast.KindConditionalType:
result = tx.transformConditionalTypeNode(input.AsConditionalTypeNode())
case ast.KindFunctionType:
result = tx.transformFunctionTypeNode(input.AsFunctionTypeNode())
case ast.KindConstructorType:
result = tx.transformConstructorTypeNode(input.AsConstructorTypeNode())
case ast.KindImportType:
result = tx.transformImportTypeNode(input.AsImportTypeNode())
case ast.KindTypeQuery:
tx.checkEntityNameVisibility(input.AsTypeQueryNode().ExprName, tx.enclosingDeclaration)
result = tx.Visitor().VisitEachChild(input)
case ast.KindTupleType:
result = tx.Visitor().VisitEachChild(input)
if result != nil {
if transformers.IsOriginalNodeSingleLine(tx.EmitContext(), input) {
tx.EmitContext().AddEmitFlags(result, printer.EFSingleLine)
}
}
case ast.KindJSDocTypeExpression:
result = tx.transformJSDocTypeExpression(input.AsJSDocTypeExpression())
case ast.KindJSDocTypeLiteral:
result = tx.transformJSDocTypeLiteral(input.AsJSDocTypeLiteral())
case ast.KindJSDocPropertyTag:
result = tx.transformJSDocPropertyTag(input.AsJSDocParameterOrPropertyTag())
case ast.KindJSDocAllType:
result = tx.transformJSDocAllType(input.AsJSDocAllType())
case ast.KindJSDocNullableType:
result = tx.transformJSDocNullableType(input.AsJSDocNullableType())
case ast.KindJSDocNonNullableType:
result = tx.transformJSDocNonNullableType(input.AsJSDocNonNullableType())
case ast.KindJSDocOptionalType:
result = tx.transformJSDocOptionalType(input.AsJSDocOptionalType())
case ast.KindJSDocVariadicType:
result = tx.transformJSDocVariadicType(input.AsJSDocVariadicType())
default:
result = tx.Visitor().VisitEachChild(input)
}
if result != nil && canProdiceDiagnostic && ast.HasDynamicName(input) {
tx.checkName(input)
}
tx.enclosingDeclaration = previousEnclosingDeclaration
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
tx.state.errorNameNode = oldName
tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType
return result
}
func (tx *DeclarationTransformer) checkName(node *ast.Node) {
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
if !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNodeName(node)
}
tx.state.errorNameNode = node.Name()
debug.Assert(ast.HasDynamicName(node)) // Should only be called with dynamic names
entityName := node.Name().AsComputedPropertyName().Expression
tx.checkEntityNameVisibility(entityName, tx.enclosingDeclaration)
if !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
}
tx.state.errorNameNode = nil
}
func (tx *DeclarationTransformer) transformMappedTypeNode(input *ast.MappedTypeNode) *ast.Node {
// handle missing template type nodes, since the printer does not
var typeNode *ast.Node
if input.Type == nil {
typeNode = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword)
} else {
typeNode = tx.Visitor().Visit(input.Type)
}
return tx.Factory().UpdateMappedTypeNode(
input,
input.ReadonlyToken,
tx.Visitor().Visit(input.TypeParameter),
tx.Visitor().Visit(input.NameType),
input.QuestionToken,
typeNode,
nil,
)
}
func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node {
retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool {
return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) ||
(clause.Token == ast.KindExtendsKeyword && t.Expression().Kind == ast.KindNullKeyword)
})
if len(retainedClauses) == 0 {
return nil // elide empty clause
}
if len(retainedClauses) == len(clause.Types.Nodes) {
return tx.Visitor().VisitEachChild(clause.AsNode())
}
return tx.Factory().UpdateHeritageClause(
clause,
tx.Visitor().VisitNodes(tx.Factory().NewNodeList(retainedClauses)),
)
}
func (tx *DeclarationTransformer) transformImportTypeNode(input *ast.ImportTypeNode) *ast.Node {
if !ast.IsLiteralImportTypeNode(input.AsNode()) {
return input.AsNode()
}
return tx.Factory().UpdateImportTypeNode(
input,
input.IsTypeOf,
tx.Factory().UpdateLiteralTypeNode(
input.Argument.AsLiteralTypeNode(),
tx.rewriteModuleSpecifier(input.AsNode(), input.Argument.AsLiteralTypeNode().Literal),
),
input.Attributes,
input.Qualifier,
tx.Visitor().VisitNodes(input.TypeArguments),
)
}
func (tx *DeclarationTransformer) transformConstructorTypeNode(input *ast.ConstructorTypeNode) *ast.Node {
return tx.Factory().UpdateConstructorTypeNode(
input,
tx.ensureModifiers(input.AsNode()),
tx.Visitor().VisitNodes(input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.Visitor().Visit(input.Type),
)
}
func (tx *DeclarationTransformer) transformFunctionTypeNode(input *ast.FunctionTypeNode) *ast.Node {
return tx.Factory().UpdateFunctionTypeNode(
input,
tx.Visitor().VisitNodes(input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.Visitor().Visit(input.Type),
)
}
func (tx *DeclarationTransformer) transformConditionalTypeNode(input *ast.ConditionalTypeNode) *ast.Node {
checkType := tx.Visitor().Visit(input.CheckType)
extendsType := tx.Visitor().Visit(input.ExtendsType)
oldEnclosingDecl := tx.enclosingDeclaration
tx.enclosingDeclaration = input.TrueType
trueType := tx.Visitor().Visit(input.TrueType)
tx.enclosingDeclaration = oldEnclosingDecl
falseType := tx.Visitor().Visit(input.FalseType)
return tx.Factory().UpdateConditionalTypeNode(
input,
checkType,
extendsType,
trueType,
falseType,
)
}
func (tx *DeclarationTransformer) transformTypeReference(input *ast.TypeReferenceNode) *ast.Node {
tx.checkEntityNameVisibility(input.TypeName, tx.enclosingDeclaration)
return tx.Visitor().VisitEachChild(input.AsNode())
}
func (tx *DeclarationTransformer) transformExpressionWithTypeArguments(input *ast.ExpressionWithTypeArguments) *ast.Node {
if ast.IsEntityName(input.Expression) || ast.IsEntityNameExpression(input.Expression) {
tx.checkEntityNameVisibility(input.Expression, tx.enclosingDeclaration)
}
return tx.Visitor().VisitEachChild(input.AsNode())
}
func (tx *DeclarationTransformer) transformTypeParameterDeclaration(input *ast.TypeParameterDeclaration) *ast.Node {
if isPrivateMethodTypeParameter(tx.host, input) && (input.DefaultType != nil || input.Constraint != nil) {
return tx.Factory().UpdateTypeParameterDeclaration(
input,
input.Modifiers(),
input.Name(),
nil,
nil,
)
}
return tx.Visitor().VisitEachChild(input.AsNode())
}
func (tx *DeclarationTransformer) transformVariableDeclaration(input *ast.VariableDeclaration) *ast.Node {
if ast.IsBindingPattern(input.Name()) {
return tx.recreateBindingPattern(input.Name().AsBindingPattern())
}
// Variable declaration types also suppress new diagnostic contexts, provided the contexts wouldn't be made for binding pattern types
tx.suppressNewDiagnosticContexts = true
return tx.Factory().UpdateVariableDeclaration(
input,
input.Name(),
nil,
tx.ensureType(input.AsNode(), false),
tx.ensureNoInitializer(input.AsNode()),
)
}
func (tx *DeclarationTransformer) recreateBindingPattern(input *ast.BindingPattern) *ast.Node {
var results []*ast.Node
for _, elem := range input.Elements.Nodes {
result := tx.recreateBindingElement(elem.AsBindingElement())
if result == nil {
continue
}
if result.Kind == ast.KindSyntaxList {
results = append(results, result.AsSyntaxList().Children...)
} else {
results = append(results, result)
}
}
if len(results) == 0 {
return nil
}
if len(results) == 1 {
return results[0]
}
return tx.Factory().NewSyntaxList(results)
}
func (tx *DeclarationTransformer) recreateBindingElement(e *ast.BindingElement) *ast.Node {
if e.Name() == nil {
return nil
}
if !getBindingNameVisible(tx.resolver, e.AsNode()) {
return nil
}
if ast.IsBindingPattern(e.Name()) {
return tx.recreateBindingPattern(e.Name().AsBindingPattern())
}
return tx.Factory().NewVariableDeclaration(
e.Name(),
nil,
tx.ensureType(e.AsNode(), false),
nil, // TODO: possible strada bug - not emitting const initialized binding pattern elements?
)
}
func (tx *DeclarationTransformer) transformIndexSignatureDeclaration(input *ast.IndexSignatureDeclaration) *ast.Node {
t := tx.Visitor().Visit(input.Type)
if t == nil {
t = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword)
}
return tx.Factory().UpdateIndexSignatureDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
tx.updateParamList(input.AsNode(), input.Parameters),
t,
)
}
func (tx *DeclarationTransformer) transformCallSignatureDeclaration(input *ast.CallSignatureDeclaration) *ast.Node {
return tx.Factory().UpdateCallSignatureDeclaration(
input,
tx.ensureTypeParams(input.AsNode(), input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.ensureType(input.AsNode(), false),
)
}
func (tx *DeclarationTransformer) transformPropertySignatureDeclaration(input *ast.PropertySignatureDeclaration) *ast.Node {
if ast.IsPrivateIdentifier(input.Name()) {
return nil
}
return tx.Factory().UpdatePropertySignatureDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
input.PostfixToken,
tx.ensureType(input.AsNode(), false),
tx.ensureNoInitializer(input.AsNode()), // TODO: possible strada bug (fixed here) - const property signatures never initialized
)
}
func (tx *DeclarationTransformer) transformPropertyDeclaration(input *ast.PropertyDeclaration) *ast.Node {
if ast.IsPrivateIdentifier(input.Name()) {
return nil
}
// Remove definite assignment assertion (!) from declaration files
postfixToken := input.PostfixToken
if postfixToken != nil && postfixToken.Kind == ast.KindExclamationToken {
postfixToken = nil
}
return tx.Factory().UpdatePropertyDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
postfixToken,
tx.ensureType(input.AsNode(), false),
tx.ensureNoInitializer(input.AsNode()),
)
}
func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.SetAccessorDeclaration) *ast.Node {
if ast.IsPrivateIdentifier(input.Name()) {
return nil
}
return tx.Factory().UpdateSetAccessorDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
nil, // accessors shouldn't have type params
tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0),
nil,
nil,
nil,
)
}
func (tx *DeclarationTransformer) transformGetAccesorDeclaration(input *ast.GetAccessorDeclaration) *ast.Node {
if ast.IsPrivateIdentifier(input.Name()) {
return nil
}
return tx.Factory().UpdateGetAccessorDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
nil, // accessors shouldn't have type params
tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0),
tx.ensureType(input.AsNode(), false),
nil,
nil,
)
}
func (tx *DeclarationTransformer) updateAccessorParamList(input *ast.Node, isPrivate bool) *ast.ParameterList {
var newParams []*ast.Node
if !isPrivate {
thisParam := ast.GetThisParameter(input)
if thisParam != nil {
newParams = append(newParams, tx.ensureParameter(thisParam.AsParameterDeclaration()))
}
}
if ast.IsSetAccessorDeclaration(input) {
var valueParam *ast.Node
if !isPrivate {
if len(newParams) == 1 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 2 {
valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[1].AsParameterDeclaration())
} else if len(newParams) == 0 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 1 {
valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[0].AsParameterDeclaration())
}
}
if valueParam == nil {
// TODO: strada bug - no type printed on set accessor missing arg as though private
var t *ast.Node
if !isPrivate {
t = tx.Factory().NewKeywordExpression(ast.KindAnyKeyword)
}
valueParam = tx.Factory().NewParameterDeclaration(
nil,
nil,
tx.Factory().NewIdentifier("value"),
nil,
t,
nil,
)
}
newParams = append(newParams, valueParam)
}
return tx.Factory().NewNodeList(newParams)
}
func (tx *DeclarationTransformer) transformConstructorDeclaration(input *ast.ConstructorDeclaration) *ast.Node {
// A constructor declaration may not have a type annotation
return tx.Factory().UpdateConstructorDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
nil, // no type params
tx.updateParamList(input.AsNode(), input.Parameters),
nil, // no return type
nil,
nil,
)
}
func (tx *DeclarationTransformer) transformConstructSignatureDeclaration(input *ast.ConstructSignatureDeclaration) *ast.Node {
return tx.Factory().UpdateConstructSignatureDeclaration(
input,
tx.ensureTypeParams(input.AsNode(), input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.ensureType(input.AsNode(), false),
)
}
func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.Node {
if input.Symbol() != nil && len(input.Symbol().Declarations) > 0 && input.Symbol().Declarations[0] != input {
return nil
} else {
return tx.Factory().NewPropertyDeclaration(
tx.ensureModifiers(input),
input.Name(),
nil,
nil,
nil,
)
}
}
func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node {
if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 {
return tx.omitPrivateMethodType(input.AsNode())
} else if ast.IsPrivateIdentifier(input.Name()) {
return nil
} else {
return tx.Factory().UpdateMethodSignatureDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
input.PostfixToken,
tx.ensureTypeParams(input.AsNode(), input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.ensureType(input.AsNode(), false),
)
}
}
func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node {
if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 {
return tx.omitPrivateMethodType(input.AsNode())
} else if ast.IsPrivateIdentifier(input.Name()) {
return nil
} else {
return tx.Factory().UpdateMethodDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
nil,
input.Name(),
input.PostfixToken,
tx.ensureTypeParams(input.AsNode(), input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.ensureType(input.AsNode(), false),
nil,
nil,
)
}
}
func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node {
// !!! TODO: stripInternal support?
// if (shouldStripInternal(input)) return nil
switch input.Kind {
case ast.KindExportDeclaration:
if ast.IsSourceFile(input.Parent) {
tx.resultHasExternalModuleIndicator = true
}
tx.resultHasScopeMarker = true
// Rewrite external module names if necessary
return tx.Factory().UpdateExportDeclaration(
input.AsExportDeclaration(),
input.Modifiers(),
input.IsTypeOnly(),
input.AsExportDeclaration().ExportClause,
tx.rewriteModuleSpecifier(input, input.AsExportDeclaration().ModuleSpecifier),
tx.tryGetResolutionModeOverride(input.AsExportDeclaration().Attributes),
)
case ast.KindExportAssignment, ast.KindJSExportAssignment:
if ast.IsSourceFile(input.Parent) {
tx.resultHasExternalModuleIndicator = true
}
tx.resultHasScopeMarker = true
if input.AsExportAssignment().Expression.Kind == ast.KindIdentifier {
return input
}
// expression is non-identifier, create _default typed variable to reference
newId := tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic})
tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
return &SymbolAccessibilityDiagnostic{
diagnosticMessage: diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
errorNode: input,
}
}
tx.tracker.PushErrorFallbackNode(input)
type_ := tx.ensureType(input, false)
varDecl := tx.Factory().NewVariableDeclaration(newId, nil, type_, nil)
tx.tracker.PopErrorFallbackNode()
var modList *ast.ModifierList
if tx.needsDeclare {
modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)})
} else {
modList = tx.Factory().NewModifierList([]*ast.Node{})
}
statement := tx.Factory().NewVariableStatement(modList, tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})))
assignment := tx.Factory().UpdateExportAssignment(input.AsExportAssignment(), input.Modifiers(), input.Type(), newId)
// Remove comments from the export declaration and copy them onto the synthetic _default declaration
tx.preserveJsDoc(statement, input)
tx.removeAllComments(assignment)
return tx.Factory().NewSyntaxList([]*ast.Node{statement, assignment})
default:
id := ast.GetNodeId(tx.EmitContext().MostOriginal(input))
if tx.lateStatementReplacementMap[id] == nil {
// Don't actually transform yet; just leave as original node - will be elided/swapped by late pass
tx.lateStatementReplacementMap[id] = tx.transformTopLevelDeclaration(input)
}
return input
}
}
func (tx *DeclarationTransformer) rewriteModuleSpecifier(parent *ast.Node, input *ast.Node) *ast.Node {
if input == nil {
return nil
}
tx.resultHasExternalModuleIndicator = tx.resultHasExternalModuleIndicator || (parent.Kind != ast.KindModuleDeclaration && parent.Kind != ast.KindImportType)
if ast.IsStringLiteralLike(input) {
if tx.isBundledEmit {
// !!! TODO: support bundled emit specifier rewriting
}
}
return input
}
func (tx *DeclarationTransformer) tryGetResolutionModeOverride(node *ast.Node) *ast.Node {
if node == nil {
return node
}
mode := tx.host.GetResolutionModeOverride(node)
if mode != core.ResolutionModeNone {
return node
}
return nil
}
func (tx *DeclarationTransformer) preserveJsDoc(updated *ast.Node, original *ast.Node) {
// !!! TODO: JSDoc comment support
// if (hasJSDocNodes(updated) && hasJSDocNodes(original)) {
// updated.jsDoc = original.jsDoc;
// }
// return setCommentRange(updated, getCommentRange(original));
}
func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) {
tx.EmitContext().AddEmitFlags(node, printer.EFNoComments)
// !!! TODO: Also remove synthetic trailing/leading comments added by transforms
// emitNode.leadingComments = undefined;
// emitNode.trailingComments = undefined;
}
func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node {
if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 {
// Private nodes emit no types (except private parameter properties, whose parameter types are actually visible)
return nil
}
if tx.shouldPrintWithInitializer(node) {
// Literal const declarations will have an initializer ensured rather than a type
return nil
}
// Should be removed createTypeOfDeclaration will actually now reuse the existing annotation so there is no real need to duplicate type walking
// Left in for now to minimize diff during syntactic type node builder refactor
if !ast.IsExportAssignment(node) && !ast.IsBindingElement(node) && node.Type() != nil && (!ast.IsParameter(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) {
return tx.Visitor().Visit(node.Type())
}
oldErrorNameNode := tx.state.errorNameNode
tx.state.errorNameNode = node.Name()
var oldDiag GetSymbolAccessibilityDiagnostic
if !tx.suppressNewDiagnosticContexts {
oldDiag = tx.state.getSymbolAccessibilityDiagnostic
if canProduceDiagnostics(node) {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node)
}
}
var typeNode *ast.Node
if hasInferredType(node) {
typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker)
} else if ast.IsFunctionLike(node) {
typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker)
} else {
debug.AssertNever(node)
}
tx.state.errorNameNode = oldErrorNameNode
if !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
}
if typeNode == nil {
return tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword)
}
return typeNode
}
func (tx *DeclarationTransformer) shouldPrintWithInitializer(node *ast.Node) bool {
return canHaveLiteralInitializer(tx.host, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node))
}
func (tx *DeclarationTransformer) checkEntityNameVisibility(entityName *ast.Node, enclosingDeclaration *ast.Node) {
visibilityResult := tx.resolver.IsEntityNameVisible(entityName, enclosingDeclaration)
tx.tracker.handleSymbolAccessibilityError(visibilityResult)
}
// Transforms the direct child of a source file into zero or more replacement statements
func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) *ast.Node {
if len(tx.state.lateMarkedStatements) > 0 {
// Remove duplicates of the current statement from the deferred work queue (this was done via orderedRemoveItem in strada - why? to ensure the same backing array? microop?)
tx.state.lateMarkedStatements = core.Filter(tx.state.lateMarkedStatements, func(node *ast.Node) bool { return node != input })
}
// !!! TODO: stripInternal support?
// if (shouldStripInternal(input)) return;
if input.Kind == ast.KindImportEqualsDeclaration {
return tx.transformImportEqualsDeclaration(input.AsImportEqualsDeclaration())
}
if input.Kind == ast.KindImportDeclaration || input.Kind == ast.KindJSImportDeclaration {
res := tx.transformImportDeclaration(input.AsImportDeclaration())
if res != nil && res.Kind != ast.KindImportDeclaration {
res := res.Clone(tx.Factory())
res.Kind = ast.KindImportDeclaration
return res
}
return res
}
if ast.IsDeclaration(input) && isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) {
return nil
}
// !!! TODO: JSDoc support
// if (isJSDocImportTag(input)) return;
// Elide implementation signatures from overload sets
if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) {
return nil
}
previousEnclosingDeclaration := tx.enclosingDeclaration
if isEnclosingDeclaration(input) {
tx.enclosingDeclaration = input
}
canProdiceDiagnostic := canProduceDiagnostics(input)
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
oldName := tx.state.errorNameNode
if canProdiceDiagnostic {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input)
}
saveNeedsDeclare := tx.needsDeclare
var result *ast.Node
switch input.Kind {
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
result = tx.transformTypeAliasDeclaration(input.AsTypeAliasDeclaration())
case ast.KindInterfaceDeclaration:
result = tx.transformInterfaceDeclaration(input.AsInterfaceDeclaration())
case ast.KindFunctionDeclaration:
result = tx.transformFunctionDeclaration(input.AsFunctionDeclaration())
case ast.KindModuleDeclaration:
result = tx.transformModuleDeclaration(input.AsModuleDeclaration())
case ast.KindClassDeclaration:
result = tx.transformClassDeclaration(input.AsClassDeclaration())
case ast.KindVariableStatement:
result = tx.transformVariableStatement(input.AsVariableStatement())
case ast.KindEnumDeclaration:
result = tx.transformEnumDeclaration(input.AsEnumDeclaration())
default:
// Anything left unhandled is an error, so this should be unreachable
panic(fmt.Sprintf("Unhandled top-level node in declaration emit: %q", input.Kind))
}
tx.enclosingDeclaration = previousEnclosingDeclaration
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
tx.needsDeclare = saveNeedsDeclare
tx.state.errorNameNode = oldName
return result
}
func (tx *DeclarationTransformer) transformTypeAliasDeclaration(input *ast.TypeAliasDeclaration) *ast.Node {
tx.needsDeclare = false
return tx.Factory().UpdateTypeAliasDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
tx.Visitor().VisitNodes(input.TypeParameters),
tx.Visitor().Visit(input.Type),
)
}
func (tx *DeclarationTransformer) transformInterfaceDeclaration(input *ast.InterfaceDeclaration) *ast.Node {
return tx.Factory().UpdateInterfaceDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
tx.Visitor().VisitNodes(input.TypeParameters),
tx.Visitor().VisitNodes(input.HeritageClauses),
tx.Visitor().VisitNodes(input.Members),
)
}
func (tx *DeclarationTransformer) transformFunctionDeclaration(input *ast.FunctionDeclaration) *ast.Node {
return tx.Factory().UpdateFunctionDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
nil,
input.Name(),
tx.ensureTypeParams(input.AsNode(), input.TypeParameters),
tx.updateParamList(input.AsNode(), input.Parameters),
tx.ensureType(input.AsNode(), false),
nil, /*fullSignature*/
nil,
)
}
func (tx *DeclarationTransformer) transformModuleDeclaration(input *ast.ModuleDeclaration) *ast.Node {
// !!! TODO: module declarations are now parsed into nested module objects with export modifiers
// It'd be good to collapse those back in the declaration output, but the AST can't represent the
// `namespace a.b.c` shape for the printer (without using invalid identifier names).
mods := tx.ensureModifiers(input.AsNode())
saveNeedsDeclare := tx.needsDeclare
tx.needsDeclare = false
inner := input.Body
keyword := input.Keyword
if keyword != ast.KindGlobalKeyword && (input.Name() == nil || !ast.IsStringLiteral(input.Name())) {
keyword = ast.KindNamespaceKeyword
}
if inner != nil && inner.Kind == ast.KindModuleBlock {
oldNeedsScopeFix := tx.needsScopeFixMarker
oldHasScopeFix := tx.resultHasScopeMarker
tx.resultHasScopeMarker = false
tx.needsScopeFixMarker = false
statements := tx.Visitor().VisitNodes(inner.AsModuleBlock().Statements)
lateStatements := tx.transformAndReplaceLatePaintedStatements(statements)
if input.Flags&ast.NodeFlagsAmbient != 0 {
tx.needsScopeFixMarker = false // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates"
}
// With the final list of statements, there are 3 possibilities:
// 1. There's an export assignment or export declaration in the namespace - do nothing
// 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers
// 3. Some things are exported, some are not, and there's no marker - add an empty marker
if !ast.IsGlobalScopeAugmentation(input.AsNode()) && !tx.resultHasScopeMarker && !hasScopeMarker(lateStatements) {
if tx.needsScopeFixMarker {
lateStatements = tx.Factory().NewNodeList(append(lateStatements.Nodes, createEmptyExports(tx.Factory().AsNodeFactory())))
} else {
lateStatements = tx.EmitContext().NewNodeVisitor(tx.stripExportModifiers).VisitNodes(lateStatements)
}
}
body := tx.Factory().UpdateModuleBlock(inner.AsModuleBlock(), lateStatements)
tx.needsDeclare = saveNeedsDeclare
tx.needsScopeFixMarker = oldNeedsScopeFix
tx.resultHasScopeMarker = oldHasScopeFix
return tx.Factory().UpdateModuleDeclaration(
input,
mods,
keyword,
input.Name(),
body,
)
}
if inner != nil {
// trigger visit. ignore result (is deferred, so is just inner unless elided)
tx.Visitor().Visit(inner)
// eagerly transform nested namespaces (the nesting doesn't need any elision or painting done)
original := tx.EmitContext().MostOriginal(inner)
id := ast.GetNodeId(original)
body, _ := tx.lateStatementReplacementMap[id]
delete(tx.lateStatementReplacementMap, id)
return tx.Factory().UpdateModuleDeclaration(
input,
mods,
keyword,
input.Name(),
body,
)
}
return tx.Factory().UpdateModuleDeclaration(
input,
mods,
keyword,
input.Name(),
nil,
)
}
func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast.Node {
if statement == nil {
return nil
}
if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(statement), ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) {
// `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace
// Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too
return statement
}
oldFlags := ast.GetCombinedModifierFlags(statement)
if oldFlags&ast.ModifierFlagsExport == 0 {
return statement
}
newFlags := oldFlags & (ast.ModifierFlagsAll ^ ast.ModifierFlagsExport)
modifiers := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier)
return ast.ReplaceModifiers(tx.Factory().AsNodeFactory(), statement, tx.Factory().NewModifierList(modifiers))
}
func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDeclaration) *ast.Node {
tx.state.errorNameNode = input.Name()
tx.tracker.PushErrorFallbackNode(input.AsNode())
defer tx.tracker.PopErrorFallbackNode()
modifiers := tx.ensureModifiers(input.AsNode())
typeParameters := tx.ensureTypeParams(input.AsNode(), input.TypeParameters)
ctor := getFirstConstructorWithBody(input.AsNode())
var parameterProperties []*ast.Node
if ctor != nil {
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes {
if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) {
continue
}
// !!! TODO: stripInternal support?
// if (shouldStripInternal(param)) { continue }
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param)
if param.Name().Kind == ast.KindIdentifier {
updated := tx.Factory().NewPropertyDeclaration(
tx.ensureModifiers(param),
param.Name(),
param.AsParameterDeclaration().QuestionToken,
tx.ensureType(param, false),
tx.ensureNoInitializer(param),
)
tx.preserveJsDoc(updated, param)
parameterProperties = append(parameterProperties, updated)
} else {
// Pattern - this is currently an error, but we emit declarations for it somewhat correctly
// !!! is this worth reimplementing? We never made it not-an-error
}
}
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
}
// When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior
// Prevents other classes with the same public members from being used in place of the current class
var privateIdentifier *ast.Node
if core.Some(input.Members.Nodes, func(member *ast.Node) bool { return member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) }) {
privateIdentifier = tx.Factory().NewPropertyDeclaration(
nil,
tx.Factory().NewPrivateIdentifier("#private"),
nil,
nil,
nil,
)
}
lateIndexes := tx.resolver.CreateLateBoundIndexSignatures(
tx.EmitContext(),
input.AsNode(),
tx.enclosingDeclaration,
declarationEmitNodeBuilderFlags,
declarationEmitInternalNodeBuilderFlags,
tx.tracker,
)
memberNodes := make([]*ast.Node, 0, len(input.Members.Nodes))
if privateIdentifier != nil {
memberNodes = append(memberNodes, privateIdentifier)
}
memberNodes = append(memberNodes, lateIndexes...)
memberNodes = append(memberNodes, parameterProperties...)
visitResult := tx.Visitor().VisitNodes(input.Members)
if visitResult != nil && len(visitResult.Nodes) > 0 {
memberNodes = append(memberNodes, visitResult.Nodes...)
}
members := tx.Factory().NewNodeList(memberNodes)
extendsClause := getEffectiveBaseTypeNode(input.AsNode())
if extendsClause != nil && !ast.IsEntityNameExpression(extendsClause.AsExpressionWithTypeArguments().Expression) && extendsClause.AsExpressionWithTypeArguments().Expression.Kind != ast.KindNullKeyword {
oldId := "default"
if ast.NodeIsPresent(input.Name()) && ast.IsIdentifier(input.Name()) && len(input.Name().AsIdentifier().Text) > 0 {
oldId = input.Name().AsIdentifier().Text
}
newId := tx.Factory().NewUniqueNameEx(oldId+"_base", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic})
tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
return &SymbolAccessibilityDiagnostic{
diagnosticMessage: diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
errorNode: extendsClause,
typeName: input.Name(),
}
}
varDecl := tx.Factory().NewVariableDeclaration(
newId,
nil,
tx.resolver.CreateTypeOfExpression(tx.EmitContext(), extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker),
nil,
)
var mods *ast.ModifierList
if tx.needsDeclare {
mods = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)})
}
statement := tx.Factory().NewVariableStatement(
mods,
tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})),
)
newHeritageClause := tx.Factory().UpdateHeritageClause(
extendsClause.Parent.AsHeritageClause(),
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().UpdateExpressionWithTypeArguments(
extendsClause.AsExpressionWithTypeArguments(),
newId,
tx.Visitor().VisitNodes(extendsClause.AsExpressionWithTypeArguments().TypeArguments),
),
}),
)
retainedHeritageClauses := tx.Visitor().VisitNodes(input.HeritageClauses) // should just be `implements`
heritageList := []*ast.Node{
newHeritageClause,
}
if retainedHeritageClauses != nil && len(retainedHeritageClauses.Nodes) > 0 {
heritageList = append(heritageList, retainedHeritageClauses.Nodes...)
}
heritageClauses := tx.Factory().NewNodeList(heritageList)
return tx.Factory().NewSyntaxList([]*ast.Node{
statement,
tx.Factory().UpdateClassDeclaration(
input,
modifiers,
input.Name(),
typeParameters,
heritageClauses,
members,
),
})
}
return tx.Factory().UpdateClassDeclaration(
input,
modifiers,
input.Name(),
typeParameters,
tx.Visitor().VisitNodes(input.HeritageClauses),
members,
)
}
func (tx *DeclarationTransformer) transformVariableStatement(input *ast.VariableStatement) *ast.Node {
visible := false
for _, decl := range input.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
visible = getBindingNameVisible(tx.resolver, decl)
if visible {
break
}
}
if !visible {
return nil
}
nodes := tx.Visitor().VisitNodes(input.DeclarationList.AsVariableDeclarationList().Declarations)
if nodes != nil && len(nodes.Nodes) == 0 {
return nil
}
modifiers := tx.ensureModifiers(input.AsNode())
var declList *ast.Node
if ast.IsVarUsing(input.DeclarationList) || ast.IsVarAwaitUsing(input.DeclarationList) {
declList = tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, nodes)
tx.EmitContext().SetOriginal(declList, input.DeclarationList)
tx.EmitContext().SetCommentRange(declList, input.DeclarationList.Loc)
declList.Loc = input.DeclarationList.Loc
} else {
declList = tx.Factory().UpdateVariableDeclarationList(input.DeclarationList.AsVariableDeclarationList(), nodes)
}
return tx.Factory().UpdateVariableStatement(input, modifiers, declList)
}
func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclaration) *ast.Node {
return tx.Factory().UpdateEnumDeclaration(
input,
tx.ensureModifiers(input.AsNode()),
input.Name(),
tx.Factory().NewNodeList(core.MapNonNil(input.Members.Nodes, func(m *ast.Node) *ast.Node {
// !!! TODO: stripInternal support?
// if (shouldStripInternal(m)) return;
// !!! TODO: isolatedDeclarations support
// if (
// isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences &&
// // This will be its own compiler error instead, so don't report.
// !isComputedPropertyName(m.name)
// ) {
// context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations));
// }
// Rewrite enum values to their constants, if available
enumValue := tx.resolver.GetEnumMemberValue(m)
var newInitializer *ast.Node
switch value := enumValue.Value.(type) {
case jsnum.Number:
if value >= 0 {
newInitializer = tx.Factory().NewNumericLiteral(value.String())
} else {
newInitializer = tx.Factory().NewPrefixUnaryExpression(
ast.KindMinusToken,
tx.Factory().NewNumericLiteral((-value).String()),
)
}
case string:
newInitializer = tx.Factory().NewStringLiteral(value)
default:
// nil
newInitializer = nil
}
result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer)
tx.preserveJsDoc(result, m)
return result
})),
)
}
func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierList {
currentFlags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsAll)
newFlags := tx.ensureModifierFlags(node)
if currentFlags == newFlags {
// Elide decorators
mods := node.Modifiers()
if mods == nil {
return mods
}
return tx.Factory().NewModifierList(core.Filter(mods.Nodes, ast.IsModifier))
}
result := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier)
if len(result) == 0 {
return nil
}
return tx.Factory().NewModifierList(result)
}
func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.ModifierFlags {
mask := ast.ModifierFlagsAll ^ (ast.ModifierFlagsPublic | ast.ModifierFlagsAsync | ast.ModifierFlagsOverride) // No async and override modifiers in declaration files
additions := ast.ModifierFlagsNone
if tx.needsDeclare && !isAlwaysType(node) {
additions = ast.ModifierFlagsAmbient
}
parentIsFile := node.Parent.Kind == ast.KindSourceFile
if !parentIsFile || (tx.isBundledEmit && parentIsFile && ast.IsExternalModule(node.Parent.AsSourceFile())) {
mask ^= ast.ModifierFlagsAmbient
additions = ast.ModifierFlagsNone
}
return maskModifierFlags(tx.host, node, mask, additions)
}
func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList {
if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 {
return nil
}
var typeParameters *ast.TypeParameterList
if typeParameters = tx.Visitor().VisitNodes(params); typeParameters != nil {
return typeParameters
}
oldErrorNameNode := tx.state.errorNameNode
tx.state.errorNameNode = node.Name()
var oldDiag GetSymbolAccessibilityDiagnostic
if !tx.suppressNewDiagnosticContexts {
oldDiag = tx.state.getSymbolAccessibilityDiagnostic
if canProduceDiagnostics(node) {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node)
}
}
if data := node.FunctionLikeData(); data != nil && data.FullSignature != nil {
if nodes := tx.resolver.CreateTypeParametersOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker); nodes != nil {
typeParameters = &ast.TypeParameterList{
Loc: node.Loc,
Nodes: nodes,
}
}
}
tx.state.errorNameNode = oldErrorNameNode
if !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
}
return typeParameters
}
func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.ParameterList) *ast.ParameterList {
if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 {
return tx.Factory().NewNodeList([]*ast.Node{})
}
results := make([]*ast.Node, len(params.Nodes))
for i, p := range params.Nodes {
results[i] = tx.ensureParameter(p.AsParameterDeclaration())
}
return tx.Factory().NewNodeList(results)
}
func (tx *DeclarationTransformer) ensureParameter(p *ast.ParameterDeclaration) *ast.Node {
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
if !tx.suppressNewDiagnosticContexts {
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.AsNode())
}
var questionToken *ast.TokenNode
if tx.resolver.IsOptionalParameter(p.AsNode()) {
if p.QuestionToken != nil {
questionToken = p.QuestionToken
} else {
questionToken = tx.Factory().NewToken(ast.KindQuestionToken)
}
}
result := tx.Factory().UpdateParameterDeclaration(
p,
nil,
p.DotDotDotToken,
tx.filterBindingPatternInitializers(p.Name()),
questionToken,
tx.ensureType(p.AsNode(), true),
tx.ensureNoInitializer(p.AsNode()),
)
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
return result
}
func (tx *DeclarationTransformer) ensureNoInitializer(node *ast.Node) *ast.Node {
if tx.shouldPrintWithInitializer(node) {
unwrappedInitializer := unwrapParenthesizedExpression(node.Initializer())
if !isPrimitiveLiteralValue(unwrappedInitializer, true) {
tx.tracker.ReportInferenceFallback(node)
}
return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker)
}
return nil
}
func (tx *DeclarationTransformer) filterBindingPatternInitializers(node *ast.Node) *ast.Node {
if node.Kind == ast.KindIdentifier {
return node
} else {
// TODO: visitor to avoid always making new nodes?
elements := make([]*ast.Node, 0, len(node.AsBindingPattern().Elements.Nodes))
for _, elem := range node.AsBindingPattern().Elements.Nodes {
if elem.Kind == ast.KindOmittedExpression {
elements = append(elements, elem)
continue
}
if elem.PropertyName() != nil && ast.IsComputedPropertyName(elem.PropertyName()) && ast.IsEntityNameExpression(elem.PropertyName().Expression()) {
tx.checkEntityNameVisibility(elem.PropertyName().Expression(), tx.enclosingDeclaration)
}
if elem.Name() == nil {
elements = append(elements, elem)
continue
}
elements = append(elements, tx.Factory().UpdateBindingElement(
elem.AsBindingElement(),
elem.AsBindingElement().DotDotDotToken,
elem.PropertyName(),
tx.filterBindingPatternInitializers(elem.Name()),
nil,
))
}
elemList := tx.Factory().NewNodeList(elements)
return tx.Factory().UpdateBindingPattern(node.AsBindingPattern(), elemList)
}
}
func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.ImportEqualsDeclaration) *ast.Node {
if !tx.resolver.IsDeclarationVisible(decl.AsNode()) {
return nil
}
if decl.ModuleReference.Kind == ast.KindExternalModuleReference {
// Rewrite external module names if necessary
specifier := ast.GetExternalModuleImportEqualsDeclarationExpression(decl.AsNode())
return tx.Factory().UpdateImportEqualsDeclaration(
decl,
decl.Modifiers(),
decl.IsTypeOnly,
decl.Name(),
tx.Factory().UpdateExternalModuleReference(decl.ModuleReference.AsExternalModuleReference(), tx.rewriteModuleSpecifier(decl.AsNode(), specifier)),
)
} else {
oldDiag := tx.state.getSymbolAccessibilityDiagnostic
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode())
tx.checkEntityNameVisibility(decl.ModuleReference, tx.enclosingDeclaration)
tx.state.getSymbolAccessibilityDiagnostic = oldDiag
return decl.AsNode()
}
}
func (tx *DeclarationTransformer) transformImportDeclaration(decl *ast.ImportDeclaration) *ast.Node {
if decl.ImportClause == nil {
// import "mod" - possibly needed for side effects? (global interface patches, module augmentations, etc)
return tx.Factory().UpdateImportDeclaration(
decl,
decl.Modifiers(),
decl.ImportClause,
tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier),
tx.tryGetResolutionModeOverride(decl.Attributes),
)
}
phaseModifier := decl.ImportClause.AsImportClause().PhaseModifier
if phaseModifier == ast.KindDeferKeyword {
phaseModifier = ast.KindUnknown
}
// The `importClause` visibility corresponds to the default's visibility.
var visibleDefaultBinding *ast.Node
if decl.ImportClause != nil && decl.ImportClause.Name() != nil && tx.resolver.IsDeclarationVisible(decl.ImportClause) {
visibleDefaultBinding = decl.ImportClause.Name()
}
if decl.ImportClause.AsImportClause().NamedBindings == nil {
// No named bindings (either namespace or list), meaning the import is just default or should be elided
if visibleDefaultBinding == nil {
return nil
}
return tx.Factory().UpdateImportDeclaration(
decl,
decl.Modifiers(),
tx.Factory().UpdateImportClause(
decl.ImportClause.AsImportClause(),
phaseModifier,
visibleDefaultBinding,
/*namedBindings*/ nil,
),
tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier),
tx.tryGetResolutionModeOverride(decl.Attributes),
)
}
if decl.ImportClause.AsImportClause().NamedBindings.Kind == ast.KindNamespaceImport {
// Namespace import (optionally with visible default)
var namedBindings *ast.Node
if tx.resolver.IsDeclarationVisible(decl.ImportClause.AsImportClause().NamedBindings) {
namedBindings = decl.ImportClause.AsImportClause().NamedBindings
}
if visibleDefaultBinding == nil && namedBindings == nil {
return nil
}
return tx.Factory().UpdateImportDeclaration(
decl,
decl.Modifiers(),
tx.Factory().UpdateImportClause(
decl.ImportClause.AsImportClause(),
phaseModifier,
visibleDefaultBinding,
namedBindings,
),
tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier),
tx.tryGetResolutionModeOverride(decl.Attributes),
)
}
// Named imports (optionally with visible default)
bindingList := core.Filter(
decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports().Elements.Nodes,
func(b *ast.Node) bool {
return tx.resolver.IsDeclarationVisible(b)
},
)
if len(bindingList) > 0 || visibleDefaultBinding != nil {
var namedImports *ast.Node
if len(bindingList) > 0 {
namedImports = tx.Factory().UpdateNamedImports(
decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports(),
tx.Factory().NewNodeList(bindingList),
)
}
return tx.Factory().UpdateImportDeclaration(
decl,
decl.Modifiers(),
tx.Factory().UpdateImportClause(
decl.ImportClause.AsImportClause(),
phaseModifier,
visibleDefaultBinding,
namedImports,
),
tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier),
tx.tryGetResolutionModeOverride(decl.Attributes),
)
}
// Augmentation of export depends on import
if tx.resolver.IsImportRequiredByAugmentation(decl) {
// IsolatedDeclarations support
// if (isolatedDeclarations) {
// context.addDiagnostic(createDiagnosticForNode(decl, Diagnostics.Declaration_emit_for_this_file_requires_preserving_this_import_for_augmentations_This_is_not_supported_with_isolatedDeclarations));
// }
return tx.Factory().UpdateImportDeclaration(
decl,
decl.Modifiers(),
/*importClause*/ nil,
tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier),
tx.tryGetResolutionModeOverride(decl.Attributes),
)
}
// Nothing visible
return nil
}
func (tx *DeclarationTransformer) transformJSDocTypeExpression(input *ast.JSDocTypeExpression) *ast.Node {
return tx.Visitor().Visit(input.Type)
}
func (tx *DeclarationTransformer) transformJSDocTypeLiteral(input *ast.JSDocTypeLiteral) *ast.Node {
members, _ := tx.Visitor().VisitSlice(input.JSDocPropertyTags)
replacement := tx.Factory().NewTypeLiteralNode(tx.Factory().NewNodeList(members))
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) transformJSDocPropertyTag(input *ast.JSDocPropertyTag) *ast.Node {
replacement := tx.Factory().NewPropertySignatureDeclaration(
nil,
tx.Visitor().Visit(input.TagName),
nil,
tx.Visitor().Visit(input.TypeExpression),
nil,
)
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) transformJSDocAllType(input *ast.JSDocAllType) *ast.Node {
replacement := tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword)
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) transformJSDocNullableType(input *ast.JSDocNullableType) *ast.Node {
replacement := tx.Factory().NewUnionTypeNode(tx.Factory().NewNodeList([]*ast.Node{
tx.Visitor().Visit(input.Type),
tx.Factory().NewLiteralTypeNode(tx.Factory().NewKeywordExpression(ast.KindNullKeyword)),
}))
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) transformJSDocNonNullableType(input *ast.JSDocNonNullableType) *ast.Node {
return tx.Visitor().Visit(input.Type)
}
func (tx *DeclarationTransformer) transformJSDocVariadicType(input *ast.JSDocVariadicType) *ast.Node {
replacement := tx.Factory().NewArrayTypeNode(tx.Visitor().Visit(input.Type))
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) transformJSDocOptionalType(input *ast.JSDocOptionalType) *ast.Node {
replacement := tx.Factory().NewUnionTypeNode(tx.Factory().NewNodeList([]*ast.Node{
tx.Visitor().Visit(input.Type),
tx.Factory().NewKeywordTypeNode(ast.KindUndefinedKeyword),
}))
tx.EmitContext().SetOriginal(replacement, input.AsNode())
return replacement
}
func (tx *DeclarationTransformer) visitExpressionStatement(node *ast.ExpressionStatement) *ast.Node {
expression := node.Expression
if expression == nil {
return nil
}
if expression.Kind == ast.KindBinaryExpression &&
ast.GetAssignmentDeclarationKind(expression.AsBinaryExpression()) == ast.JSDeclarationKindProperty {
return tx.transformExpandoAssignment(expression.AsBinaryExpression())
}
return nil
}
func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExpression) *ast.Node {
left := node.Left
symbol := node.Symbol
if symbol == nil || symbol.Flags&ast.SymbolFlagsAssignment == 0 {
return nil
}
ns := ast.GetLeftmostAccessExpression(left)
if ns == nil || ns.Kind != ast.KindIdentifier {
return nil
}
declaration := tx.resolver.GetReferencedValueDeclaration(ns)
if declaration == nil {
return nil
}
host := declaration.Symbol()
if host == nil {
return nil
}
name := tx.Factory().NewIdentifier(ns.Text())
property := tx.tryGetPropertyName(left)
if property == "" || !scanner.IsIdentifierText(property, core.LanguageVariantStandard) {
return nil
}
tx.transformExpandoHost(name, declaration)
if ast.IsFunctionDeclaration(declaration) && !shouldEmitFunctionProperties(declaration.AsFunctionDeclaration()) {
return nil
}
isNonContextualKeywordName := ast.IsNonContextualKeyword(scanner.StringToToken(property))
exportName := core.IfElse(isNonContextualKeywordName, tx.Factory().NewGeneratedNameForNode(left), tx.Factory().NewIdentifier(property))
synthesizedNamespace := tx.Factory().NewModuleDeclaration(nil /*modifiers*/, ast.KindNamespaceKeyword, name, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList([]*ast.Node{})))
synthesizedNamespace.Parent = tx.enclosingDeclaration
declarationData := synthesizedNamespace.DeclarationData()
declarationData.Symbol = host
containerData := synthesizedNamespace.LocalsContainerData()
containerData.Locals = make(ast.SymbolTable, 0)
saveDiag := tx.state.getSymbolAccessibilityDiagnostic
tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node.AsNode())
t := tx.resolver.CreateTypeOfExpression(tx.EmitContext(), left, synthesizedNamespace, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags|nodebuilder.InternalFlagsNoSyntacticPrinter, tx.tracker)
tx.state.getSymbolAccessibilityDiagnostic = saveDiag
statements := []*ast.Statement{
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
ast.NodeFlagsNone,
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewVariableDeclaration(exportName, nil /*exclamationToken*/, t, nil /*initializer*/),
}),
),
),
}
if isNonContextualKeywordName {
namedExports := tx.Factory().NewNamedExports(tx.Factory().NewNodeList(
[]*ast.Node{
tx.Factory().NewExportSpecifier(false /*isTypeOnly*/, exportName, tx.Factory().NewIdentifier(left.Name().Text())),
},
))
statements = append(statements, tx.Factory().NewExportDeclaration(nil /*modifiers*/, false /*isTypeOnly*/, namedExports, nil /*moduleSpecifier*/, nil /*attributes*/))
}
flags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(declaration), ast.ModifierFlagsAll)
modifierFlags := ast.ModifierFlagsAmbient
if flags&ast.ModifierFlagsExport != 0 {
if flags&ast.ModifierFlagsDefault == 0 {
modifierFlags |= ast.ModifierFlagsExport
}
tx.resultHasScopeMarker = true
tx.resultHasExternalModuleIndicator = true
}
return tx.Factory().NewModuleDeclaration(tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier)), ast.KindNamespaceKeyword, name, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList(statements)))
}
func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declaration *ast.Declaration) {
root := core.IfElse(ast.IsVariableDeclaration(declaration), declaration.Parent.Parent, declaration)
id := ast.GetNodeId(tx.EmitContext().MostOriginal(root))
if tx.expandoHosts.Has(id) {
return
}
saveNeedsDeclare := tx.needsDeclare
tx.needsDeclare = true
modifierFlags := tx.ensureModifierFlags(root)
defaultExport := modifierFlags&ast.ModifierFlagsExport != 0 && modifierFlags&ast.ModifierFlagsDefault != 0
tx.needsDeclare = saveNeedsDeclare
if defaultExport {
modifierFlags |= ast.ModifierFlagsAmbient
modifierFlags ^= ast.ModifierFlagsDefault
modifierFlags ^= ast.ModifierFlagsExport
}
modifiers := tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier))
replacement := make([]*ast.Node, 0)
if ast.IsFunctionDeclaration(declaration) {
typeParameters, parameters, asteriskToken := extractExpandoHostParams(declaration)
replacement = append(replacement, tx.Factory().UpdateFunctionDeclaration(declaration.AsFunctionDeclaration(), modifiers, asteriskToken, declaration.Name(), tx.ensureTypeParams(declaration, typeParameters), tx.updateParamList(declaration, parameters), tx.ensureType(declaration, false), nil /*fullSignature*/, nil /*body*/))
} else if ast.IsVariableDeclaration(declaration) && ast.IsFunctionExpressionOrArrowFunction(declaration.Initializer()) {
fn := declaration.Initializer()
typeParameters, parameters, asteriskToken := extractExpandoHostParams(fn)
replacement = append(replacement, tx.Factory().NewFunctionDeclaration(modifiers, asteriskToken, tx.Factory().NewIdentifier(name.Text()), tx.ensureTypeParams(fn, typeParameters), tx.updateParamList(fn, parameters), tx.ensureType(fn, false), nil /*fullSignature*/, nil /*body*/))
} else {
return
}
if defaultExport {
replacement = append(replacement, tx.Factory().NewExportAssignment(nil /*modifiers*/, false /*isExportEquals*/, nil /*typeNode*/, name))
}
tx.expandoHosts.Add(id)
tx.lateStatementReplacementMap[id] = tx.Factory().NewSyntaxList(replacement)
}
func extractExpandoHostParams(node *ast.Node) (typeParameters *ast.TypeParameterList, parameters *ast.ParameterList, asteriskToken *ast.TokenNode) {
switch node.Kind {
case ast.KindFunctionExpression:
fn := node.AsFunctionExpression()
return fn.TypeParameters, fn.Parameters, fn.AsteriskToken
case ast.KindArrowFunction:
fn := node.AsArrowFunction()
return fn.TypeParameters, fn.Parameters, fn.AsteriskToken
default:
fn := node.AsFunctionDeclaration()
return fn.TypeParameters, fn.Parameters, fn.AsteriskToken
}
}
func (tx *DeclarationTransformer) tryGetPropertyName(node *ast.Node) string {
if ast.IsElementAccessExpression(node) {
return tx.resolver.GetElementAccessExpressionName(node.AsElementAccessExpression())
}
if ast.IsPropertyAccessExpression(node) {
return node.Name().Text()
}
return ""
}