package declarations import ( "fmt" "iter" "slices" "strings" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/debug" "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/modulespecifiers" "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/transformers" "github.com/microsoft/typescript-go/internal/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 thisPropertyAssignmentKey struct { name string node *ast.Node isStatic bool isPrivate bool } func getThisPropertyAssignmentKey(name *ast.Node, node *ast.Node, isStatic bool) thisPropertyAssignmentKey { isPrivate := ast.IsPrivateIdentifier(name) if name != nil && !ast.IsDynamicName(name) { if nameText, ok := ast.TryGetTextOfPropertyName(name); ok { return thisPropertyAssignmentKey{name: nameText, isStatic: isStatic, isPrivate: isPrivate} } } return thisPropertyAssignmentKey{node: node, isStatic: isStatic, isPrivate: isPrivate} } type DeclarationTransformer struct { transformers.Transformer host DeclarationEmitHost compilerOptions *core.CompilerOptions tracker *SymbolTrackerImpl state *SymbolTrackerSharedState resolver printer.EmitResolver declarationFilePath string declarationMapPath string needsDeclare bool needsScopeFixMarker bool resultHasScopeMarker bool enclosingDeclaration *ast.Node resultHasExternalModuleIndicator bool suppressNewDiagnosticContexts bool witnessedCjsExports collections.Set[string] lateStatementReplacementMap map[ast.NodeId]*ast.Node expandoHosts map[ast.NodeId]*ast.Node // store the result of transforming expando hosts so they can be inserted later if the host is actually referenced expandoMembers map[ast.NodeId][]*ast.Node // store any found expando _members_ after transforming them so *if* the host is referenced, they can be emitted alongside it deferredExpandoAssignments map[ast.NodeId][]*ast.BinaryExpression // expando assignments whose host wasn't visible when collected, processed if the host is late-marked visible seenProperties collections.Set[thisPropertyAssignmentKey] thisPropertyAssignmentsCollected []*ast.Node rawReferencedFiles []ReferencedFilePair rawTypeReferenceDirectives []*ast.FileReference rawLibReferenceDirectives []*ast.FileReference bindingNameVisitor *ast.NodeVisitor expressionVisitor *ast.NodeVisitor cjsExportAssignmentVisitor *ast.NodeVisitor exportStrippingVisitor *ast.NodeVisitor thisPropertyVisitor *ast.NodeVisitor cjsExportAssignment *ast.Node cjsExportMembers []*ast.Node cjsExportAssignmentName *ast.Node // tracks the name node used for `export =` in CJS module.exports assignments declareStrippingVisitor *ast.NodeVisitor inClassExpressionDeclaration bool // true when serializing members of a class expression kept as a class declaration } // 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(), stripInternal: compilerOptions.StripInternal.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.state.reportExpandoFunctionErrors = func(node *ast.Node) { if !tx.state.isolatedDeclarations { return } props := resolver.GetPropertiesOfContainerFunction(node) for _, p := range props { if ast.IsExpandoPropertyDeclaration(p.ValueDeclaration) { errorTarget := p.ValueDeclaration if ast.IsBinaryExpression(errorTarget) { errorTarget = errorTarget.AsBinaryExpression().Left } tx.state.addDiagnostic(createDiagnosticForNode(errorTarget, diagnostics.Assigning_properties_to_functions_without_declaring_them_is_not_supported_with_isolatedDeclarations_Add_an_explicit_declaration_for_the_properties_assigned_to_this_function)) } } } tx.NewTransformer(tx.visit, context) tx.bindingNameVisitor = tx.EmitContext().NewNodeVisitor(tx.visitBindingName) tx.expressionVisitor = tx.EmitContext().NewNodeVisitor(tx.visitNestedExpression) tx.exportStrippingVisitor = tx.EmitContext().NewNodeVisitor(tx.stripExportModifiers) tx.thisPropertyVisitor = tx.EmitContext().NewNodeVisitor(tx.visitThisPropertyAssignments) tx.cjsExportAssignmentVisitor = tx.EmitContext().NewNodeVisitor(tx.visitCJSExportAssignments) tx.declareStrippingVisitor = tx.EmitContext().NewNodeVisitor(tx.stripDeclareModifiers) return tx } func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic { return tx.state.diagnostics } func (tx *DeclarationTransformer) shouldStripInternal(node *ast.Node) bool { return tx.state.stripInternal && node != nil && tx.isInternalDeclaration(node, tx.state.currentSourceFile) } func (tx *DeclarationTransformer) isInternalDeclaration(node *ast.Node, sourceFile *ast.SourceFile) bool { if node == nil { return false } parseTreeNode := tx.EmitContext().MostOriginal(node) if !ast.IsParseTreeNode(parseTreeNode) { return false } if parseTreeNode.Kind == ast.KindParameter { params := parseTreeNode.Parent.Parameters() paramIdx := slices.IndexFunc(params, func(p *ast.ParameterDeclarationNode) bool { return p.AsNode() == parseTreeNode }) var previousSibling *ast.Node if paramIdx > 0 { previousSibling = params[paramIdx-1].AsNode() } text := sourceFile.Text() var commentRanges []ast.CommentRange if previousSibling != nil { // to handle // ... parameters, /** @internal */ // public param: string trailingPos := scanner.SkipTriviaEx(text, previousSibling.End()+1, &scanner.SkipTriviaOptions{StopAtComments: true}) for comment := range scanner.GetTrailingCommentRanges(tx.Factory().AsNodeFactory(), text, trailingPos) { commentRanges = append(commentRanges, comment) } for comment := range scanner.GetLeadingCommentRanges(tx.Factory().AsNodeFactory(), text, node.Pos()) { commentRanges = append(commentRanges, comment) } } else { trailingPos := scanner.SkipTriviaEx(text, node.Pos(), &scanner.SkipTriviaOptions{StopAtComments: true}) for comment := range scanner.GetTrailingCommentRanges(tx.Factory().AsNodeFactory(), text, trailingPos) { commentRanges = append(commentRanges, comment) } } if len(commentRanges) > 0 { return hasInternalAnnotation(commentRanges[len(commentRanges)-1], sourceFile) } return false } for commentRange := range tx.getLeadingCommentRangesOfNode(parseTreeNode, sourceFile) { if hasInternalAnnotation(commentRange, sourceFile) { return true } } return false } func (tx *DeclarationTransformer) getLeadingCommentRangesOfNode(node *ast.Node, sourceFile *ast.SourceFile) iter.Seq[ast.CommentRange] { if node == nil || node.Kind == ast.KindJsxText { return nil } return scanner.GetLeadingCommentRanges(tx.Factory().AsNodeFactory(), sourceFile.Text(), node.Pos()) } func hasInternalAnnotation(commentRange ast.CommentRange, sourceFile *ast.SourceFile) bool { comment := sourceFile.Text()[commentRange.Pos():commentRange.End()] return strings.Contains(comment, "@internal") } 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 } 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.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, ast.KindExpressionStatement: return nil // 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 { tx.cjsExportAssignmentName = nil if node.IsDeclarationFile { return node.AsNode() } 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 = make(map[ast.NodeId]*ast.Node) tx.expandoMembers = make(map[ast.NodeId][]*ast.Node) tx.deferredExpandoAssignments = make(map[ast.NodeId][]*ast.BinaryExpression) tx.rawReferencedFiles = make([]ReferencedFilePair, 0) tx.rawTypeReferenceDirectives = make([]*ast.FileReference, 0) tx.rawLibReferenceDirectives = make([]*ast.FileReference, 0) tx.witnessedCjsExports.Clear() tx.state.currentSourceFile = node tx.collectFileReferences(node) tx.resolver.PrecalculateDeclarationEmitVisibility(tx.EmitContext().MostOriginal(node.AsNode()).AsSourceFile()) 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 nodeOrSyntaxListChildren(node *ast.Node) []*ast.Node { if ast.IsSyntaxList(node) { return node.AsSyntaxList().Children } return []*ast.Node{node} } func flattenSyntaxLists(nodes []*ast.Node) []*ast.Node { return core.FlatMap(nodes, nodeOrSyntaxListChildren) } func (tx *DeclarationTransformer) appendCjsExports(combinedStatements *ast.StatementList) *ast.StatementList { result := []*ast.Node{} if tx.cjsExportAssignment != nil { result = append(result, tx.cjsExportAssignment) } result = append(result, tx.cjsExportMembers...) result = append(result, combinedStatements.Nodes...) statementNodes := flattenSyntaxLists(result) if len(statementNodes) != len(combinedStatements.Nodes) { combinedStatements = tx.Factory().NewNodeList(statementNodes) } return combinedStatements } func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast.Node { tx.cjsExportAssignment = nil tx.cjsExportAssignmentName = nil tx.cjsExportMembers = nil defer func() { tx.cjsExportAssignment = nil tx.cjsExportAssignmentName = nil tx.cjsExportMembers = nil }() tx.cjsExportAssignmentVisitor.VisitNode(node.AsNode()) // collect nested module.exports= assignments tx.expressionVisitor.VisitNode(node.AsNode()) // collect expando members (requires any export assignment be located in advance) var combinedStatements *ast.StatementList statements := tx.Visitor().VisitNodes(node.Statements) combinedStatements = tx.transformAndReplaceLatePaintedStatements(statements) combinedStatements = tx.appendCjsExports(combinedStatements) combinedStatements.Loc = statements.Loc // setTextRange if ast.IsExternalOrCommonJSModule(node) { if ast.IsInJSFile(node.AsNode()) { if exportEquals := node.Symbol.Exports[ast.InternalSymbolNameExportEquals]; exportEquals != nil && len(exportEquals.Declarations) > 1 { for _, node := range exportEquals.Declarations { tx.state.addDiagnostic(createDiagnosticForNode(node, diagnostics.Multiple_module_exports_assignments_cannot_be_serialized_for_declaration_emit)) } } } if !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) 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(elem) { 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 { 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) setupDiagnosticContext(input *ast.Node) (bool, func()) { 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 } return canProdiceDiagnostic, func() { tx.state.getSymbolAccessibilityDiagnostic = oldDiag tx.state.errorNameNode = oldName tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType } } func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node { if tx.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) { tx.state.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 tx.state.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().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 } if ast.IsHeritageClause(input) && (len(input.AsHeritageClause().Types.Nodes) == 0 || (len(input.AsHeritageClause().Types.Nodes) == 1 && ast.NodeIsMissing(input.AsHeritageClause().Types.Nodes[0]))) { return nil } previousEnclosingDeclaration := tx.enclosingDeclaration if isEnclosingDeclaration(input) { tx.enclosingDeclaration = input } canProdiceDiagnostic, cleanupDiagnosticContext := tx.setupDiagnosticContext(input) defer cleanupDiagnosticContext() 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.AsTypeParameterDeclaration()) case ast.KindExpressionWithTypeArguments: result = tx.transformExpressionWithTypeArguments(input.AsExpressionWithTypeArguments()) case ast.KindTypeReference: result = tx.transformTypeReference(input.AsTypeReferenceNode()) 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 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().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, clause.Token, 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, input.Expression, nil, ) } return tx.Visitor().VisitEachChild(input.AsNode()) } func (tx *DeclarationTransformer) transformVariableDeclaration(input *ast.VariableDeclaration) *ast.Node { if tx.state.currentSourceFile.CommonJSModuleIndicator != nil && ast.IsVariableDeclarationInitializedToRequire(input.AsNode()) { return tx.transformCjsRequireVariableDeclaration(input) } 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) transformCjsRequireVariableDeclaration(input *ast.VariableDeclaration) *ast.Node { specifier := tx.rewriteModuleSpecifier(input.AsNode(), input.Initializer.AsCallExpression().Arguments.Nodes[0]) if ast.IsIdentifier(input.Name()) { // `const x = require("something")` -> `import x = require("something")` return tx.Factory().NewImportEqualsDeclaration(nil, false, input.Name(), tx.Factory().NewExternalModuleReference(specifier)) } else if ast.IsArrayBindingPattern(input.Name()) { // TODO: Is this actually reachable? should we error on this? return nil } else { // object binding pattern // `const {x, y: z} = require("something")` -> `import {x, y as z} from "something"` b := input.Name().AsBindingPattern() var importSpecifiers []*ast.Node for _, elem := range b.Elements.Nodes { if !ast.IsIdentifier(elem.Name()) { continue // nested destructuring, bail } importSpecifiers = append(importSpecifiers, tx.Factory().NewImportSpecifier(false, elem.PropertyName(), elem.Name())) } return tx.Factory().NewImportDeclaration( nil, tx.Factory().NewImportClause( ast.KindUnknown, nil, tx.Factory().NewNamedImports(tx.Factory().NewNodeList(importSpecifiers)), ), specifier, nil, ) } } 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 } result := 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 ) tx.preservePartialJsDoc(result, input.AsNode()) return result } 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 { // When synthesizing a missing value parameter, emit `value: any` for non-private accessors to match TypeScript's declaration emit behavior. var t *ast.Node if !isPrivate { t = tx.Factory().NewKeywordTypeNode(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 } result := tx.Factory().NewPropertyDeclaration( tx.ensureModifiers(input), input.Name(), nil, nil, nil, ) tx.preserveJsDoc(result, input) return result } 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 { if tx.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.ModuleSpecifier()), tx.tryGetResolutionModeOverride(input.AsExportDeclaration().Attributes), ) case ast.KindExportAssignment: return tx.transformExportAssignment(input, input, input.Expression(), input.AsExportAssignment().IsExportEquals) 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) tryGetNameOfAssignedExpression(unwrapped *ast.Node) *ast.Node { var nameNode *ast.Node var nameText string if !ast.IsPropertyAccessExpression(unwrapped) && unwrapped.Name() != nil { nameText = unwrapped.Name().Text() } else if ast.IsIdentifier(unwrapped) { nameText = unwrapped.Text() } if nameText != "" && nameText != "default" { if tx.resolver.IsNameResolvable(tx.enclosingDeclaration, nameText) { // create a unique name that shares the same text as its' base nameNode = tx.Factory().NewUniqueNameEx(nameText, printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) } else { // use the node's name as-is, since it's not otherwise in-scope nameNode = tx.Factory().NewIdentifier(nameText) } } return nameNode } func (tx *DeclarationTransformer) getNameOfExportedAssignedExpression(unwrapped *ast.Node, isExportEquals bool) *ast.Node { nameNode := tx.tryGetNameOfAssignedExpression(unwrapped) if nameNode == nil { // fallback to a default name if isExportEquals && ast.IsSourceFileJS(tx.state.currentSourceFile) { // only JS files prefer to use `_exports` for export assignments - TS has always used `_default` for both `export=` and `export default` nameNode = tx.Factory().NewUniqueNameEx("_exports", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) } else { nameNode = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) } } tx.cjsExportAssignmentName = nameNode return nameNode } func (tx *DeclarationTransformer) transformExportAssignment(input *ast.Node, assignment *ast.Node, expression *ast.Node, isExportEquals bool) *ast.Node { if ast.IsSourceFile(input.Parent) { tx.resultHasExternalModuleIndicator = true } tx.resultHasScopeMarker = true if ast.IsIdentifier(expression) && (ast.IsSourceFile(input.Parent) || ast.IsModuleBlock(input.Parent)) { exportAssignment := tx.Factory().NewExportAssignment(nil, isExportEquals, nil, expression) tx.preserveJsDoc(exportAssignment, input) return exportAssignment } // Check if the expression is a class expression - emit as a class declaration + export assignment unwrapped := ast.SkipOuterExpressions(expression, ast.OEKExpressionTypePassthrough) newId := tx.getNameOfExportedAssignedExpression(unwrapped, isExportEquals) if ast.IsClassExpression(unwrapped) { var mods []*ast.Node if tx.needsDeclare { mods = append(mods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } classDecl := tx.transformClassExpressionToDeclaration(unwrapped, newId, tx.Factory().NewModifierList(mods)) tx.preserveJsDoc(classDecl, input) // Reuse the same name node for the export so unique names resolve consistently exportAssignment := tx.Factory().NewExportAssignment(nil, isExportEquals, nil, newId) tx.removeAllComments(exportAssignment) return tx.Factory().NewSyntaxList([]*ast.Node{exportAssignment, classDecl}) } else if ast.IsFunctionLike(unwrapped) { // Promote function or arrow function expressions to a function declaration var mods []*ast.Node if tx.needsDeclare { mods = append(mods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } funcDecl := tx.transformFunctionLikeToDeclaration(unwrapped, newId, tx.Factory().NewModifierList(mods)) tx.preserveJsDoc(funcDecl, input) // Reuse the same name node for the export so unique names resolve consistently exportAssignment := tx.Factory().NewExportAssignment(nil, isExportEquals, nil, newId) tx.removeAllComments(exportAssignment) return tx.Factory().NewSyntaxList([]*ast.Node{exportAssignment, funcDecl}) } // expression is non-identifier, create _default typed variable to reference 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.cjsExportAssignmentName = newId tx.tracker.PushErrorFallbackNode(assignment) var type_, initializer *ast.Node if ast.IsPrimitiveLiteralValue(unwrapParenthesizedExpression(expression), true) { initializer = tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(assignment), tx.tracker) } if initializer == nil { type_ = tx.ensureType(assignment, false) } varDecl := tx.Factory().NewVariableDeclaration(newId, nil, type_, initializer) 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(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsConst)) exportAssignment := tx.Factory().NewExportAssignment(nil, isExportEquals, nil, newId) // Remove comments from the export declaration and copy them onto the synthetic _default declaration tx.preserveJsDoc(statement, input) return tx.Factory().NewSyntaxList([]*ast.Node{statement, exportAssignment}) } func (tx *DeclarationTransformer) transformFunctionLikeToDeclaration(unwrapped *ast.Node, funcName *ast.Node, mods *ast.ModifierList) *ast.Node { d := unwrapped.FunctionLikeData() return tx.Factory().NewFunctionDeclaration( mods, nil, funcName, tx.ensureTypeParams(unwrapped, d.TypeParameters), tx.updateParamList(unwrapped, d.Parameters), tx.ensureType(unwrapped, false), tx.Visitor().VisitNode(d.FullSignature), nil, ) } func (tx *DeclarationTransformer) transformBinaryExpressionToExportDeclaration(input *ast.Node, name *ast.Node) *ast.Node { propertyName := input.AsBinaryExpression().Right // track alias target so referenced declarations are included in the output tx.tracker.handleSymbolAccessibilityError(tx.resolver.IsEntityNameVisible(propertyName, tx.enclosingDeclaration)) if ast.IsIdentifier(name) && propertyName.Text() == name.Text() { propertyName = nil } return tx.Factory().NewExportDeclaration( nil, false, tx.Factory().NewNamedExports(tx.Factory().NewNodeList([]*ast.Node{tx.Factory().NewExportSpecifier(false, propertyName, name)})), nil, nil, ) } func (tx *DeclarationTransformer) transformCommonJSExport(input *ast.Node, name *ast.Node) *ast.Node { res := tx.transformCommonJSExportWorker(input, name) if res == nil { return res } return tx.wrapInCJSExportNamespace(res) } func (tx *DeclarationTransformer) transformCommonJSExportWorker(input *ast.Node, name *ast.Node) *ast.Node { var nameText string if ast.IsIdentifier(name) || ast.IsStringLiteral(name) { nameText = name.Text() } if tx.witnessedCjsExports.Has(nameText) && nameText != "" { return nil // Already emitted this export name } tx.witnessedCjsExports.Add(nameText) tx.resultHasExternalModuleIndicator = true tx.resultHasScopeMarker = true // only transform cjs exports to shorthand at the top-level of a source file, otherwise we uniformly emit nested exports with a type annotation if isCommonJSAliasExport(input) && ast.IsExpressionStatement(input.Parent) && ast.IsSourceFile(input.Parent.Parent) { // export { name } // export { source as name } return tx.transformBinaryExpressionToExportDeclaration(input, name) } // Check if the RHS is a class expression - emit as a class declaration instead of a typed variable if ast.IsBinaryExpression(input) { if rhs := unwrapParenthesizedExpression(input.AsBinaryExpression().Right); ast.IsClassExpression(rhs) { ce := rhs.AsClassExpression() classExprName := ce.Name() hasExprName := classExprName != nil && len(classExprName.Text()) > 0 if hasExprName { // Set up TrackSymbol watch to detect if the class expression's own // symbol is referenced during member type serialization. tx.tracker.watchedClassSymbol = rhs.Symbol() tx.tracker.classSymbolTracked = false defer func() { tx.tracker.watchedClassSymbol = nil tx.tracker.classSymbolTracked = false }() // Serialize class members using the class expression name, which // triggers TrackSymbol for any self-referential member types. className := tx.Factory().NewIdentifier(classExprName.Text()) classMods := []*ast.Node{tx.Factory().NewModifier(ast.KindExportKeyword)} classDecl := tx.transformClassExpressionToDeclaration(rhs, className, tx.Factory().NewModifierList(classMods)) tx.preserveJsDoc(classDecl, input) // Determine if namespace isolation is needed: // - The class expression name differs from the export name, OR // - The class's own symbol was used in a member's serialized type namesDiffer := !ast.IsIdentifier(name) || classExprName.Text() != name.Text() needsIsolation := namesDiffer || tx.tracker.classSymbolTracked if needsIsolation { nsName := tx.Factory().NewUniqueNameEx("_ns", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) var nsMods []*ast.Node if tx.needsDeclare { nsMods = append(nsMods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } nsDecl := tx.Factory().NewModuleDeclaration( tx.Factory().NewModifierList(nsMods), ast.KindNamespaceKeyword, nsName, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList([]*ast.Node{classDecl})), ) aliasBase := "_exported" if nameText := name.Text(); ast.IsIdentifier(name) && scanner.IsIdentifierText("_"+nameText, core.LanguageVariantStandard) { aliasBase = "_" + nameText } importAlias := tx.Factory().NewUniqueNameEx(aliasBase, printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) qualifiedName := tx.Factory().NewQualifiedName(nsName, className) importDecl := tx.Factory().NewImportEqualsDeclaration(nil, false, importAlias, qualifiedName) exportSpecifier := tx.Factory().NewExportSpecifier(false, importAlias, name) exportDecl := tx.Factory().NewExportDeclaration(nil, false, tx.Factory().NewNamedExports(tx.Factory().NewNodeList([]*ast.Node{exportSpecifier})), nil, nil) tx.removeAllComments(exportDecl) return tx.Factory().NewSyntaxList(append([]*ast.Node{nsDecl, importDecl}, exportDecl)) } // No isolation needed: names match and no self-references. // Update modifiers to include declare if needed. var mods []*ast.Node mods = append(mods, tx.Factory().NewModifier(ast.KindExportKeyword)) if tx.needsDeclare { mods = append(mods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } classDecl = tx.Factory().UpdateClassDeclaration( classDecl.AsClassDeclaration(), tx.Factory().NewModifierList(mods), classDecl.AsClassDeclaration().Name(), classDecl.AsClassDeclaration().TypeParameters, classDecl.AsClassDeclaration().HeritageClauses, classDecl.AsClassDeclaration().Members, ) return classDecl } var mods []*ast.Node mods = append(mods, tx.Factory().NewModifier(ast.KindExportKeyword)) if tx.needsDeclare { mods = append(mods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } className := name if !ast.IsIdentifier(className) { className = tx.Factory().NewUniqueNameEx("_class", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) } classDecl := tx.transformClassExpressionToDeclaration(rhs, className, tx.Factory().NewModifierList(mods)) tx.preserveJsDoc(classDecl, input) if !ast.IsIdentifier(name) { // Non-identifier name: emit class declaration + named export exportDecl := tx.Factory().NewExportDeclaration(nil, false, tx.Factory().NewNamedExports(tx.Factory().NewNodeList([]*ast.Node{tx.Factory().NewExportSpecifier(false, className, name)})), nil, nil) tx.removeAllComments(exportDecl) return tx.Factory().NewSyntaxList([]*ast.Node{classDecl, exportDecl}) } return classDecl } } if ast.IsIdentifier(name) { if name.Text() == "default" { // const _default: Type; export default _default; 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(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsConst)) assignment := tx.Factory().NewExportAssignment(input.Modifiers(), false, nil, 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}) } else if tx.host.GetEmitResolver().GetReferencedValueDeclaration(name) == input || tx.host.GetEmitResolver().GetReferencedValueDeclaration(name) == nil { // only inline to a export var if the `name` lookup points at this assignment or nothing - if it points at something else, we must use a temp name // export var name: Type tx.tracker.PushErrorFallbackNode(input) type_ := tx.ensureType(input, false) varDecl := tx.Factory().NewVariableDeclaration(name, nil, type_, nil) tx.tracker.PopErrorFallbackNode() var modList *ast.ModifierList if tx.needsDeclare { modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindExportKeyword), tx.Factory().NewModifier(ast.KindDeclareKeyword)}) } else { modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindExportKeyword)}) } return tx.Factory().NewVariableStatement(modList, tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsNone)) } } // const _exported: Type; export {_exported as "name"}; newId := tx.Factory().NewUniqueNameEx("_exported", 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(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsConst)) assignment := tx.Factory().NewExportDeclaration(nil, false, tx.Factory().NewNamedExports(tx.Factory().NewNodeList([]*ast.Node{tx.Factory().NewExportSpecifier(false, newId, name)})), nil, nil) // 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}) } func (tx *DeclarationTransformer) wrapInCJSExportNamespace(content *ast.Node) *ast.Node { if tx.cjsExportAssignmentName == nil { return content } // Reuse the same name node so unique names resolve consistently with the class/export nsName := tx.cjsExportAssignmentName var members []*ast.Node if content.Kind == ast.KindSyntaxList { members = content.AsSyntaxList().Children } else { members = []*ast.Node{content} } var nsMods []*ast.Node if tx.needsDeclare { nsMods = append(nsMods, tx.Factory().NewModifier(ast.KindDeclareKeyword)) } members, _ = tx.declareStrippingVisitor.VisitSlice(members) return tx.Factory().NewModuleDeclaration( tx.Factory().NewModifierList(nsMods), ast.KindNamespaceKeyword, nsName, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList(members)), ) } func isCommonJSAliasExport(node *ast.Node) bool { if ast.IsBinaryExpression(node) && ast.IsIdentifier(node.AsBinaryExpression().Right) { if symbol := node.Symbol(); symbol != nil && len(symbol.Declarations) == 1 { return true } } return false } // transformClassExpressionToDeclaration converts a class expression into a class declaration // for use in CJS export declarations (e.g., exports.K = class K {} or module.exports = class Thing {}). // This delegates to the shared buildClassMembers helper to stay in sync with transformClassDeclaration. func (tx *DeclarationTransformer) transformClassExpressionToDeclaration(classExpr *ast.Node, className *ast.Node, modifiers *ast.ModifierList) *ast.Node { previousEnclosingDeclaration := tx.enclosingDeclaration tx.enclosingDeclaration = classExpr previousInClassExpressionDeclaration := tx.inClassExpressionDeclaration tx.inClassExpressionDeclaration = true defer func() { tx.enclosingDeclaration = previousEnclosingDeclaration tx.inClassExpressionDeclaration = previousInClassExpressionDeclaration }() var extraMembers []*ast.Node if ast.IsInJSFile(classExpr) { extraMembers = tx.collectThisPropertyAssignments(classExpr) } members := tx.buildClassMembers(classExpr, extraMembers...) typeParameters := tx.ensureTypeParams(classExpr, classExpr.AsClassExpression().TypeParameters) heritageClauses := tx.Visitor().VisitNodes(classExpr.AsClassExpression().HeritageClauses) return tx.Factory().NewClassDeclaration( modifiers, className, typeParameters, heritageClauses, members, ) } 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) 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) { // Copy comment range from original to updated node so JSDoc comments are preserved tx.EmitContext().AssignCommentRange(updated, original) } func (tx *DeclarationTransformer) preservePartialJsDoc(updated *ast.Node, original *ast.Node) { if original.Flags&ast.NodeFlagsReparsed == 0 { return } jsdoc := core.FirstOrNil(original.EagerJSDoc(ast.GetSourceFileOfNode(original))) if jsdoc == nil { return } description := scanner.GetTextOfJSDocComment(jsdoc.AsJSDoc().Comment) if description == "" { return } comment := "*\n * " + strings.ReplaceAll(description, "\n", "\n * ") + "\n " tx.EmitContext().AddSyntheticLeadingComment(updated, ast.KindMultiLineCommentTrivia, comment, true /*hasTrailingNewLine*/) } 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.IsParameterDeclaration(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) { if tx.state.currentSourceFile.IsJS() { // JS types have a heap of constructs we can't directly emit into .d.ts files; the node builder contains logic to remap those where possible, so we invoke it here // In strada we always built js declarations symbolically, so all js type nodes went through this postprocessing jsFlags := declarationEmitNodeBuilderFlags if tx.inClassExpressionDeclaration { jsFlags &^= nodebuilder.FlagsWriteClassExpressionAsTypeLiteral } res := tx.resolver.TryJSTypeNodeToTypeNode(tx.EmitContext(), node.Type(), tx.enclosingDeclaration, jsFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) if res != nil { return res } // otherwise, fall back to full serialization } else { 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 flags := declarationEmitNodeBuilderFlags if tx.inClassExpressionDeclaration { flags &^= nodebuilder.FlagsWriteClassExpressionAsTypeLiteral } if ast.HasInferredType(node) { typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, flags, declarationEmitInternalNodeBuilderFlags, tx.tracker) } else if ast.IsFunctionLike(node) { typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, flags, 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 }) } if tx.shouldStripInternal(input) { return nil } 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 } original := tx.EmitContext().MostOriginal(input) id := ast.GetNodeId(original) _, isExpandoHost := tx.expandoHosts[id] _, hasDeferredExpandoAssignments := tx.deferredExpandoAssignments[id] if isExpandoHost || hasDeferredExpandoAssignments { return tx.createFullExpandoBlock(id) } 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 { if tx.resolver.IsExpandoFunctionDeclaration(input.AsNode()) { tx.state.reportExpandoFunctionErrors(input.AsNode()) } 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.StatementList()) 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.exportStrippingVisitor.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 } parseNode := tx.EmitContext().ParseNode(statement) if ast.IsImportEqualsDeclaration(statement) || (parseNode != nil && tx.host.GetEffectiveDeclarationFlags(parseNode, 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)) } // buildClassMembers builds the member list for a class-like node (ClassDeclaration or ClassExpression). // It handles parameter properties, private identifiers, late-bound index signatures, and visited members. // Extra members (e.g., this-property assignments from JS files) can be passed via extraMembers. func (tx *DeclarationTransformer) buildClassMembers(classNode *ast.Node, extraMembers ...*ast.Node) *ast.NodeList { ctor := ast.GetFirstConstructorWithBody(classNode) var parameterProperties []*ast.Node if ctor != nil { oldDiag := tx.state.getSymbolAccessibilityDiagnostic for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes { if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) || tx.shouldStripInternal(param) { continue } tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param) if param.Name().Kind == ast.KindIdentifier { updated := tx.Factory().NewPropertyDeclaration( tx.ensureModifiers(param), param.Name(), param.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 parameterProperties = append(parameterProperties, tx.walkBindingPattern(param.Name().AsBindingPattern(), param)...) } } 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(classNode.ClassLikeData().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(), classNode, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker, ) memberNodes := make([]*ast.Node, 0, len(classNode.ClassLikeData().Members.Nodes)) if privateIdentifier != nil { memberNodes = append(memberNodes, privateIdentifier) } memberNodes = append(memberNodes, lateIndexes...) memberNodes = append(memberNodes, parameterProperties...) memberNodes = append(memberNodes, extraMembers...) visitResult := tx.Visitor().VisitNodes(classNode.ClassLikeData().Members) if visitResult != nil && len(visitResult.Nodes) > 0 { memberNodes = append(memberNodes, visitResult.Nodes...) } return tx.Factory().NewNodeList(memberNodes) } func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDeclaration) *ast.Node { previousEnclosingDeclaration := tx.enclosingDeclaration tx.enclosingDeclaration = input.AsNode() defer func() { tx.enclosingDeclaration = previousEnclosingDeclaration }() 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) // Collect this.x property assignments from constructors and static blocks in JS files var extraMembers []*ast.Node if ast.IsInJSFile(input.AsNode()) { extraMembers = tx.collectThisPropertyAssignments(input.AsNode()) } members := tx.buildClassMembers(input.AsNode(), extraMembers...) extendsClause := getEffectiveBaseTypeNode(input.AsNode()) if extendsClause != nil && !ast.IsEntityNameExpression(extendsClause.AsExpressionWithTypeArguments().Expression) && extendsClause.AsExpressionWithTypeArguments().Expression.Kind != ast.KindNullKeyword { tx.tracker.ReportInferenceFallback(extendsClause.AsExpressionWithTypeArguments().Expression) // Add an isolated declarations error on this extends clause oldId := "default" if ast.NodeIsPresent(input.Name()) && ast.IsIdentifier(input.Name()) && len(input.Name().Text()) > 0 { oldId = input.Name().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(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsConst), ) newHeritageClause := tx.Factory().UpdateHeritageClause( extendsClause.Parent.AsHeritageClause(), extendsClause.Parent.AsHeritageClause().Token, 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) visitThisPropertyAssignments(node *ast.Node) *ast.Node { var thisTarget *ast.Node isStatic := false thisContainer := ast.GetThisContainer(node, false, false) thisTarget = thisContainer.Parent if thisTarget == nil { return nil // thisContainer was source file, can't have expando-this } if ast.HasStaticModifier(thisContainer) || ast.IsClassStaticBlockDeclaration(thisContainer) { isStatic = true } if thisTarget != tx.enclosingDeclaration { return nil // stop searching within new `this` contexts } caseBlock: switch ast.GetAssignmentDeclarationKind(node) { case ast.JSDeclarationKindThisProperty: name := ast.GetNameOfDeclaration(node) base := tx.resolver.GetReferencedMemberValueDeclaration(node) key := getThisPropertyAssignmentKey(name, node, isStatic) if base == nil || tx.seenProperties.Has(key) { break } tx.seenProperties.Add(key) // problem: this prop might be overriding a prop from a base type. The checker has special bails for override compat comparisons for binary expression properties, // but what we transform to won't - so we either need to match the base type (for example, if it's a getter/setter) or emit nothing // See `checkKindsOfPropertyMemberOverrides` in the checker for what we're trying to satisfy here if thisTarget.ClassLikeData().HeritageClauses != nil && len(thisTarget.ClassLikeData().HeritageClauses.Nodes) > 0 && !isClassExtendingNull(thisTarget) { // there is a base type any assignments might be "from" tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information if tx.resolver.IsThisPropertyAssignmentDeclarationRedundant(node) { break caseBlock // skip assignments whose member is already provided by an `extends` base type (an inherited accessor/method, or an identical inherited property) // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } var mods *ast.ModifierList if isStatic { mods = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindStaticKeyword)}) } if ast.HasDynamicName(node) { if !transformers.IsSimpleInlineableExpression(name) { break // Member either becomes an index signature or is a reassignment } tx.checkName(node) name = tx.Factory().NewComputedPropertyName(name) // Convert `this[foo] = expr` to `[foo]: Type` } if ast.GetTextOfPropertyName(name) == "constructor" { break // `constructor` is a builtin class member, not allowed to redeclare it } if ast.IsIdentifier(name) && !scanner.IsIdentifierText(name.Text(), core.LanguageVariantStandard) { name = tx.Factory().NewStringLiteralFromNode(name) } prop := tx.Factory().NewPropertyDeclaration( mods, name, nil, tx.ensureType(node, false), nil, ) if ast.IsExpressionStatement(node.Parent) { tx.preserveJsDoc(prop, node.Parent) } tx.thisPropertyAssignmentsCollected = append(tx.thisPropertyAssignmentsCollected, prop) } return tx.thisPropertyVisitor.VisitEachChild(node) } func isClassExtendingNull(node *ast.Node) bool { if node == nil { return false } heritage := node.ClassLikeData().HeritageClauses if heritage == nil { return false } if len(heritage.Nodes) > 1 || len(heritage.Nodes) == 0 { return false } for _, expA := range heritage.Nodes[0].AsHeritageClause().Types.Nodes { expr := expA.AsExpressionWithTypeArguments().Expression if expr != nil && expr.Kind == ast.KindNullKeyword { return true } } return false } // collectThisPropertyAssignments finds `this.x = expr` assignments in constructors, methods, and static blocks // of JS classes and synthesizes PropertyDeclaration nodes for each unique property name. func (tx *DeclarationTransformer) collectThisPropertyAssignments(classNode *ast.Node) []*ast.Node { members := classNode.ClassLikeData().Members seen := collections.Set[thisPropertyAssignmentKey]{} // Pre-populate seen with existing direct member nodes to avoid duplicates for _, member := range members.Nodes { if member.Name() != nil { isStatic := ast.IsStatic(member) seen.Add(getThisPropertyAssignmentKey(member.Name(), member, isStatic)) } } tx.seenProperties = seen defer tx.seenProperties.Clear() tx.thisPropertyAssignmentsCollected = []*ast.Node{} defer func() { tx.thisPropertyAssignmentsCollected = nil }() for _, n := range members.Nodes { tx.thisPropertyVisitor.VisitEachChild(n) } return tx.thisPropertyAssignmentsCollected } func (tx *DeclarationTransformer) walkBindingPattern(pattern *ast.BindingPattern, param *ast.Node) []*ast.Node { var elems []*ast.Node for _, elem := range pattern.Elements.Nodes { if ast.IsOmittedExpression(elem) { continue } if ast.IsBindingPattern(elem.Name()) { elems = append(elems, tx.walkBindingPattern(elem.Name().AsBindingPattern(), param)...) continue } elems = append(elems, tx.Factory().NewPropertyDeclaration( tx.ensureModifiers(param), elem.Name(), nil, /*questionOrExclamationToken*/ tx.ensureType(elem, false), nil, /*initializer*/ )) } return elems } 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 } inputNodes := input.DeclarationList.AsVariableDeclarationList().Declarations.Nodes var extraImports []*ast.Node if tx.state.currentSourceFile.CommonJSModuleIndicator != nil { var normalDeclarations []*ast.Node var imports []*ast.Node for _, n := range inputNodes { if ast.IsVariableDeclarationInitializedToRequire(n) { imports = append(imports, n) } else { normalDeclarations = append(normalDeclarations, n) } } inputNodes = normalDeclarations extraImports, _ = tx.Visitor().VisitSlice(imports) } nodes, _ := tx.Visitor().VisitSlice(inputNodes) if len(nodes) == 0 { if len(extraImports) > 0 { return tx.Factory().NewSyntaxList(extraImports) } return nil } nodeList := tx.Factory().NewNodeList(nodes) modifiers := tx.ensureModifiers(input.AsNode()) var declList *ast.Node if ast.IsVarUsing(input.DeclarationList) || ast.IsVarAwaitUsing(input.DeclarationList) { declList = tx.Factory().NewVariableDeclarationList(nodeList, ast.NodeFlagsConst) 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(), nodeList, input.DeclarationList.Flags) } res := tx.Factory().UpdateVariableStatement(input, modifiers, declList) if len(extraImports) > 0 { return tx.Factory().NewSyntaxList(append(extraImports, res)) } return res } 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 { if tx.shouldStripInternal(m) { return nil } // Rewrite enum values to their constants, if available enumValue := tx.resolver.GetEnumMemberValue(m) if tx.state.isolatedDeclarations && m.Initializer() != nil && enumValue.HasExternalReferences && // This will be its own compiler error instead, so don't report. !ast.IsComputedPropertyName(m.Name()) { tx.state.addDiagnostic(createDiagnosticForNode(m, diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)) } var newInitializer *ast.Node switch value := enumValue.Value.(type) { case jsnum.Number: if value.IsInf() { if value > 0 { newInitializer = tx.Factory().NewIdentifier("Infinity") } else { newInitializer = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity")) } } else if value.IsNaN() { newInitializer = tx.Factory().NewIdentifier("NaN") } else if value >= 0 { newInitializer = tx.Factory().NewNumericLiteral(value.String(), ast.TokenFlagsNone) } else { newInitializer = tx.Factory().NewPrefixUnaryExpression( ast.KindMinusToken, tx.Factory().NewNumericLiteral((-value).String(), ast.TokenFlagsNone), ) } case string: newInitializer = tx.Factory().NewStringLiteral(value, ast.TokenFlagsNone) 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 := ast.GetCombinedModifierFlags(tx.EmitContext().ParseNode(node)) & ast.ModifierFlagsAll newFlags := tx.ensureModifierFlags(node) if currentFlags == newFlags { // Elide decorators mods := node.Modifiers() if mods == nil { return mods } if canReuseModifierNodes(mods.Nodes) { 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 { mask ^= ast.ModifierFlagsAmbient additions = ast.ModifierFlagsNone } if ast.IsImplicitlyExportedJSDocDeclaration(node) { additions |= ast.ModifierFlagsExport } return maskModifierFlags(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.bindingNameVisitor.VisitNode(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 !ast.IsPrimitiveLiteralValue(unwrappedInitializer, true) { tx.tracker.ReportInferenceFallback(node) } return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker) } return nil } func (tx *DeclarationTransformer) visitBindingName(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindIdentifier, ast.KindOmittedExpression: return node case ast.KindArrayBindingPattern, ast.KindObjectBindingPattern: return node.VisitEachChild(tx.bindingNameVisitor) case ast.KindBindingElement: if node.PropertyName() != nil && ast.IsComputedPropertyName(node.PropertyName()) && ast.IsEntityNameExpression(node.PropertyName().Expression()) { tx.checkEntityNameVisibility(node.PropertyName().Expression(), tx.enclosingDeclaration) } return tx.Factory().UpdateBindingElement(node.AsBindingElement(), node.AsBindingElement().DotDotDotToken, node.PropertyName(), tx.bindingNameVisitor.VisitNode(node.Name()), nil /*initializer*/) default: return node } } 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.Elements(), 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) { if tx.state.isolatedDeclarations { tx.state.addDiagnostic(createDiagnosticForNode(decl.AsNode(), 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.JSDocParameterOrPropertyTag) *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) getNameExpressionPreferringIdentifier(nameExpr *ast.Node) *ast.Node { if ast.IsNumericLiteral(nameExpr) { // Numeric property names are string properties in JS; convert to string literal nameExpr = tx.Factory().NewStringLiteral(nameExpr.Text(), ast.TokenFlagsNone) } if ast.IsStringLiteralLike(nameExpr) && scanner.IsIdentifierText(nameExpr.Text(), core.LanguageVariantStandard) { result := tx.Factory().NewIdentifier(nameExpr.Text()) // prefer non-string literal names where possible kwKind := scanner.IdentifierToKeywordKind(result.AsIdentifier()) // keep keywords as strings, except `default`, which has special reformulations in the transformer if kwKind == ast.KindUnknown || kwKind == ast.KindDefaultKeyword { // fake this into a parse tree node so the reference resolver resolves the node via `resolveName` result.Parent = nameExpr.Parent result.Flags &^= ast.NodeFlagsSynthesized // intentionally leave Loc unset so the string isn't used as the text source of the identifier return result } } return nameExpr } func isNotDeclareModifier(mod *ast.Modifier) bool { return mod.Kind != ast.KindDeclareKeyword } func (tx *DeclarationTransformer) stripDeclareModifiers(node *ast.Node) *ast.Node { if node == nil { return nil } mods := node.Modifiers() if mods != nil { flags := node.ModifierFlags() if flags&ast.ModifierFlagsAmbient != 0 { filtered := core.Filter(mods.Nodes, isNotDeclareModifier) node.AsMutable().SetModifiers(tx.Factory().NewModifierList(filtered)) } } return node // no need to recur into children, only strip at top-level } func (tx *DeclarationTransformer) visitCJSExportAssignments(expression *ast.Node) *ast.Node { if expression != nil { _, cleanupDiagnosticContext := tx.setupDiagnosticContext(expression) defer cleanupDiagnosticContext() switch ast.GetAssignmentDeclarationKind(expression) { case ast.JSDeclarationKindModuleExports: if tx.state.currentSourceFile.CommonJSModuleIndicator != nil { result := tx.transformExportAssignment(expression.Parent, expression, expression.AsBinaryExpression().Right, true /*isExportEquals*/) if result != nil { tx.cjsExportAssignment = result tx.resultHasScopeMarker = true tx.resultHasExternalModuleIndicator = true } } } return tx.cjsExportAssignmentVisitor.VisitEachChild(expression) // recur through the whole tree, looking for module.exports= } return nil } func (tx *DeclarationTransformer) visitNestedExpression(expression *ast.Node) *ast.Node { if expression != nil { _, cleanupDiagnosticContext := tx.setupDiagnosticContext(expression) defer cleanupDiagnosticContext() switch ast.GetAssignmentDeclarationKind(expression) { case ast.JSDeclarationKindProperty: tx.transformExpandoAssignment(expression.AsBinaryExpression()) case ast.JSDeclarationKindExportsProperty: if tx.state.currentSourceFile.CommonJSModuleIndicator != nil { result := tx.transformCommonJSExport(expression, tx.getNameExpressionPreferringIdentifier(ast.GetElementOrPropertyAccessName(expression.AsBinaryExpression().Left))) if result != nil { tx.cjsExportMembers = append(tx.cjsExportMembers, result) } } case ast.JSDeclarationKindObjectDefinePropertyExports: if tx.state.currentSourceFile.CommonJSModuleIndicator != nil { result := tx.transformCommonJSExport(expression, tx.getNameExpressionPreferringIdentifier(expression.Arguments()[1])) if result != nil { tx.cjsExportMembers = append(tx.cjsExportMembers, result) } } } return tx.expressionVisitor.VisitEachChild(expression) // recur through the whole tree, looking for special assignments } return nil } func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExpression) { left := node.Left symbol := node.Symbol if symbol == nil || symbol.Flags&ast.SymbolFlagsAssignment == 0 { return } ns := ast.GetLeftmostAccessExpression(left) if ns == nil || ns.Kind != ast.KindIdentifier { return } declaration := tx.resolver.GetReferencedValueDeclaration(ns) if declaration == nil { return } if tx.shouldStripInternal(declaration) { return } if ast.IsVariableDeclaration(declaration) && declaration.Type() != nil { return } if ast.IsFunctionDeclaration(declaration) && declaration.FunctionLikeData().FullSignature != nil { return } if ast.IsVariableDeclaration(declaration) && !ast.IsFunctionLike(declaration.Initializer()) { return // We're going to add a type, no need to dupe members with a namespace } host := declaration.Symbol() if host == nil { return } name := tx.Factory().NewIdentifier(ns.Text()) property := tx.tryGetPropertyName(left) if property == "" || !scanner.IsIdentifierText(property, core.LanguageVariantStandard) { return } hostId := tx.getExpandoHostId(declaration) if ast.IsDeclaration(declaration) && isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, declaration) { // The host isn't visible (yet) - printing the type of a visible declaration may still // late-mark it as visible (e.g. an exported variable whose type prints as `typeof host`), // so defer the assignment to be processed if and when that happens. tx.deferredExpandoAssignments[hostId] = append(tx.deferredExpandoAssignments[hostId], node) return } if ast.IsFunctionDeclaration(declaration) && !shouldEmitFunctionProperties(declaration.AsFunctionDeclaration()) { return } tx.transformExpandoHost(name, declaration) exportName := tx.Factory().NewIdentifier(property) localName := tx.tryGetNameOfAssignedExpression(node.AsNode()) if localName == nil && !tx.resolver.IsNameResolvable(tx.enclosingDeclaration, property) && !ast.IsNonContextualKeyword(scanner.StringToToken(exportName.Text())) { // use exportName as localName if there won't be any conflicts or keyword issues localName = exportName } if localName == nil || ast.IsNonContextualKeyword(scanner.StringToToken(localName.Text())) { // fallback to a generated name if the localName doesn't exist or is a keyword localName = tx.Factory().NewGeneratedNameForNode(node.AsNode()) } _, cleanupDiagnosticContext := tx.setupDiagnosticContext(node.AsNode()) defer cleanupDiagnosticContext() if ast.IsIdentifier(node.Right) { // alias-like, emit an `export {name}` or `export {name as alias}` result := tx.transformBinaryExpressionToExportDeclaration(node.AsNode(), exportName) tx.expandoMembers[hostId] = append(tx.expandoMembers[hostId], result) return } preexistingExpandoHasExport := core.Some(tx.expandoMembers[hostId], ast.IsExportDeclaration) var varModifiers *ast.ModifierList if preexistingExpandoHasExport { varModifiers = tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(ast.ModifierFlagsExport, tx.Factory().NewModifier)) } 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) containerData.Locals[localName.Text()] = symbol oldEnclosing := tx.enclosingDeclaration tx.enclosingDeclaration = synthesizedNamespace defer func() { tx.enclosingDeclaration = oldEnclosing }() statements := []*ast.Statement{ tx.Factory().NewVariableStatement( varModifiers, tx.Factory().NewVariableDeclarationList( tx.Factory().NewNodeList([]*ast.Node{ tx.Factory().NewVariableDeclaration(localName, nil /*exclamationToken*/, tx.ensureType(node.AsNode(), false), nil /*initializer*/), }), ast.NodeFlagsNone, ), ), } if localName.Text() != exportName.Text() { namedExports := tx.Factory().NewNamedExports(tx.Factory().NewNodeList( []*ast.Node{ tx.Factory().NewExportSpecifier(false /*isTypeOnly*/, localName, exportName), }, )) statements = append(statements, tx.Factory().NewExportDeclaration(nil /*modifiers*/, false /*isTypeOnly*/, namedExports, nil /*moduleSpecifier*/, nil /*attributes*/)) } if len(statements) > 1 && !preexistingExpandoHasExport { // Add an `export` modifier to all existing expando members so they remain exported after the `export {}` is added for _, decl := range tx.expandoMembers[hostId] { modifierFlags := ast.ModifierFlagsExport | ast.GetCombinedModifierFlags(decl) decl.AsMutable().SetModifiers(tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier))) } } tx.expandoMembers[hostId] = append(tx.expandoMembers[hostId], statements...) } func (tx *DeclarationTransformer) getExpandoHostId(declaration *ast.Declaration) ast.NodeId { root := core.IfElse(ast.IsVariableDeclaration(declaration), declaration.Parent.Parent, declaration) id := ast.GetNodeId(tx.EmitContext().MostOriginal(root)) return id } func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declaration *ast.Declaration) { root := core.IfElse(ast.IsVariableDeclaration(declaration), declaration.Parent.Parent, declaration) id := tx.getExpandoHostId(declaration) if _, ok := tx.expandoHosts[id]; ok { 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 } _, cleanupDiagnosticContext := tx.setupDiagnosticContext(declaration) defer cleanupDiagnosticContext() 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 { tx.expandoHosts[id] = tx.transformTopLevelDeclaration(declaration) return } tx.state.reportExpandoFunctionErrors(declaration) if defaultExport { if ast.IsSourceFile(declaration.Parent) { tx.resultHasExternalModuleIndicator = true } tx.resultHasScopeMarker = true replacement = append(replacement, tx.Factory().NewExportAssignment(nil /*modifiers*/, false /*isExportEquals*/, nil /*typeNode*/, name)) } // store host result to be added to the output when it's actually visited tx.expandoHosts[id] = tx.Factory().NewSyntaxList(replacement) if _, ok := tx.lateStatementReplacementMap[id]; ok { tx.lateStatementReplacementMap[id] = tx.createFullExpandoBlock(id) } } func (tx *DeclarationTransformer) createFullExpandoBlock(id ast.NodeId) *ast.Node { // Process any expando assignments on this host that were skipped because it wasn't // visible when they were collected - if it's still not visible, they simply get // re-deferred, and are dropped if the host is never late-marked visible. if deferred, ok := tx.deferredExpandoAssignments[id]; ok { delete(tx.deferredExpandoAssignments, id) for _, assignment := range deferred { tx.transformExpandoAssignment(assignment) } } n := tx.expandoHosts[id] if addOns, ok := tx.expandoMembers[id]; ok { var modifiers *ast.ModifierList var name *ast.Node var host []*ast.Node if n != nil && n.Kind == ast.KindSyntaxList { // find the first named syntax list element and use its' name & modifiers for c := range n.AsSyntaxList().IterChildren() { if c.Name() != nil { name = c.Name().Clone(tx.Factory()) if c.Modifiers() != nil { modifiers = c.Modifiers().Clone(tx.Factory().AsNodeFactory()) } break } } host = n.AsSyntaxList().Children } else if n != nil { name = n.Name().Clone(tx.Factory()) if n.Modifiers() != nil { modifiers = n.Modifiers().Clone(tx.Factory().AsNodeFactory()) } host = []*ast.Node{n} } if name != nil { moduleDecl := tx.Factory().NewModuleDeclaration( modifiers, ast.KindNamespaceKeyword, name, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList(addOns)), ) members := append(host, moduleDecl) return tx.Factory().NewSyntaxList(members) } } return n } 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 "" }