2958 lines
115 KiB
Go
2958 lines
115 KiB
Go
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 ""
|
|
}
|