Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ func (r *EmitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool {
// If the node is not exported or it is not ambient module element (except import declaration)
if r.checker.getCombinedModifierFlagsCached(node)&ast.ModifierFlagsExport == 0 &&
!(node.Kind != ast.KindImportEqualsDeclaration && parent.Kind != ast.KindSourceFile && parent.Flags&ast.NodeFlagsAmbient != 0) {
// A declaration that is the target of an `export =` in its containing external
// module is externally visible, even though it isn't itself marked `export`.
// This mirrors the visibility marking done for export assignments, which would
// otherwise be skipped for modules that are referenced but never emitted.
if r.isExportEqualsTargetDeclaration(node) {
return true
}
return ast.IsGlobalSourceFile(parent)
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
Expand Down Expand Up @@ -233,6 +240,25 @@ func (r *EmitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool {
}
}

// isExportEqualsTargetDeclaration reports whether the given declaration's symbol is the
// entity referenced by an `export =` in its containing external module. Such a declaration
// is externally visible because the `export =` re-exports it.
func (r *EmitResolver) isExportEqualsTargetDeclaration(node *ast.Node) bool {

@weswigham Wesley Wigham (weswigham) Jul 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge fan of this - it reads like a kludgy workaround that doesn't actually fix the root cause. determineIfDeclarationIsVisible doesn't have any symbolic logic like this in it directly thus far - it's all just AST walking and recursion. markLinkedAliases in the emit resolver (which is analogous to the strada checker's collectLinkedAliases, not to be confused with markLinkedReferences which is for tracking import name usage for import elision) would seem to be the place where we should be updating our logic to handle cross-file NS merges better - likely by following imported names to their origin file and marking linked aliases on those, too.

Basically, what I'd recommend is having PrecalculateDeclarationEmitVisibility call file.AsNode().ForEachChild(r.aliasMarkingVisitor) not just on the input file, but on any files the input file references, so those namespace-relevant links get set (recursively - which'll require tweaking the locking and caching a bit, but should be totally doable). You can also remove the TODO from that, since this case pretty conclusively shows that cross-file NS merging (since we added it back in) requires this be an upfront walk.

symbol := r.checker.getSymbolOfDeclaration(node)
if symbol == nil {
return false
}
moduleSymbol := r.checker.getExternalModuleContainer(node)
if moduleSymbol == nil || moduleSymbol.Exports == nil {
return false
}
exportEquals := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals]
if exportEquals == nil {
return false
}
return r.checker.getSymbolIfSameReference(exportEquals, symbol) != nil
}

func (r *EmitResolver) PrecalculateDeclarationEmitVisibility(file *ast.SourceFile) {
r.checkerMu.Lock()
defer r.checkerMu.Unlock()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
Object.defineProperty(exports, "__esModule", { value: true });


//// [a.d.ts]
declare module "foo" {
function f(): T;
}
export {};
//// [b.d.ts]
import * as foo from "foo";
declare module "foo" {
Expand Down

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion testdata/submoduleTriaged.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ compiler/declarationEmitTypeofRest.errors.txt.diff
# Corsa changes how module augmentation interacts with export-equals and exported name visibility
## https://github.com/microsoft/typescript-go/issues/3481
compiler/augmentExportEquals2.errors.txt.diff
compiler/exportAssignmentMembersVisibleInAugmentation.errors.txt.diff

# Corsa no longer emits the TS-1 internal diagnostic about pre-emit/post-emit diagnostic count mismatches
## https://github.com/microsoft/typescript-go/issues/3508
Expand Down