Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
56 changes: 49 additions & 7 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5539,7 +5539,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) {
links := c.moduleSymbolLinks.Get(moduleSymbol)
if !links.exportsChecked {
exportEqualsSymbol := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals]
if exportEqualsSymbol != nil && c.hasExportedMembers(moduleSymbol) {
if exportEqualsSymbol != nil && c.hasExportedMembers(moduleSymbol, exportEqualsSymbol.ValueDeclaration.Kind == ast.KindJSExportAssignment) {
declaration := core.OrElse(c.getDeclarationOfAliasSymbol(exportEqualsSymbol), exportEqualsSymbol.ValueDeclaration)
if declaration != nil && !isTopLevelInExternalModuleAugmentation(declaration) {
c.error(declaration, diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements)
Expand Down Expand Up @@ -5575,10 +5575,17 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) {
}
}

func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool {
func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol, isCommonJS bool) bool {
for id := range moduleSymbol.Exports {
if id != ast.InternalSymbolNameExportEquals {
return true
if !isCommonJS {
return true
}
for _, declaration := range moduleSymbol.Exports[id].Declarations {
if declaration.Kind != ast.KindJSTypeAliasDeclaration {
return true
}
}
}
}
return false
Expand Down Expand Up @@ -15333,13 +15340,27 @@ func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ign
if resolveLocation == nil {
resolveLocation = name
}
symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), meaning, message, true /*isUse*/, false /*excludeGlobals*/))
if meaning == ast.SymbolFlagsNamespace {
symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), meaning, nil, true /*isUse*/, false /*excludeGlobals*/))
if symbol == nil {
alias := c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), ast.SymbolFlagsAlias, nil, true /*isUse*/, false /*excludeGlobals*/))
if alias != nil && alias.Name == ast.InternalSymbolNameExportEquals {
// resolve typedefs exported from commonjs, stored on the module symbol
symbol = alias.Parent
}
}
if symbol == nil && message != nil {
c.resolveName(resolveLocation, name.Text(), meaning, message, true /*isUse*/, false /*excludeGlobals*/)
}
} else {
symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), meaning, message, true /*isUse*/, false /*excludeGlobals*/))
}
case ast.KindQualifiedName:
qualified := name.AsQualifiedName()
symbol = c.resolveQualifiedName(name, qualified.Left, qualified.Right, meaning, ignoreErrors, dontResolveAlias, location)
symbol = c.resolveQualifiedName(name, qualified.Left, qualified.Right, meaning, ignoreErrors, location)
case ast.KindPropertyAccessExpression:
access := name.AsPropertyAccessExpression()
symbol = c.resolveQualifiedName(name, access.Expression, access.Name(), meaning, ignoreErrors, dontResolveAlias, location)
symbol = c.resolveQualifiedName(name, access.Expression, access.Name(), meaning, ignoreErrors, location)
default:
panic("Unknown entity name kind")
}
Expand All @@ -15357,7 +15378,7 @@ func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ign
return symbol
}

func (c *Checker) resolveQualifiedName(name *ast.Node, left *ast.Node, right *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *ast.Node) *ast.Symbol {
func (c *Checker) resolveQualifiedName(name *ast.Node, left *ast.Node, right *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, location *ast.Node) *ast.Symbol {
namespace := c.resolveEntityName(left, ast.SymbolFlagsNamespace, ignoreErrors, false /*dontResolveAlias*/, location)
if namespace == nil || ast.NodeIsMissing(right) {
return nil
Expand Down Expand Up @@ -15713,12 +15734,26 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as
}
return symbols
}
var originalModule *ast.Symbol
if moduleSymbol != nil {
if c.resolveSymbolEx(moduleSymbol.Exports[ast.InternalSymbolNameExportEquals], false /*dontResolveAlias*/) != nil {
originalModule = moduleSymbol
}
}
// A module defined by an 'export=' consists of one export that needs to be resolved
moduleSymbol = c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/)
exports = visit(moduleSymbol, nil, false)
if exports == nil {
exports = make(ast.SymbolTable)
}
// A CommonJS module defined by an 'export=' might also export typedefs, stored on the original module
if originalModule != nil && len(originalModule.Exports) > 1 {
for _, symbol := range originalModule.Exports {
if symbol.Flags&ast.SymbolFlagsType != 0 && symbol.Name != ast.InternalSymbolNameExportEquals && exports[symbol.Name] == nil {
exports[symbol.Name] = symbol
}
}
}
for name := range nonTypeOnlyNames.Keys() {
delete(typeOnlyExportStarMap, name)
}
Expand Down Expand Up @@ -23972,6 +24007,13 @@ func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type {
symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(mergedResolvedSymbol), current.Text(), false /*skipObjectFunctionPropertyAugment*/, true /*includeTypeOnlyMembers*/)
} else {
symbolFromModule = c.getSymbol(c.getExportsOfSymbol(mergedResolvedSymbol), current.Text(), meaning)
if symbolFromModule == nil {
// a CommonJS module might have typedefs exported alongside an export=
immediateModuleSymbol := c.resolveExternalModuleSymbol(innerModuleSymbol, true /*dontResolveAlias*/)
if immediateModuleSymbol != nil && core.Some(immediateModuleSymbol.Declarations, func(d *ast.Node) bool { return d.Kind == ast.KindJSExportAssignment }) {
symbolFromModule = c.getSymbol(c.getExportsOfSymbol(immediateModuleSymbol.Parent), current.Text(), meaning)
}
}
}
next := core.OrElse(symbolFromModule, symbolFromVariable)
if next == nil {
Expand Down
1 change: 0 additions & 1 deletion internal/fourslash/_scripts/failingTests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ TestIndexerReturnTypes1
TestIndirectClassInstantiation
TestInstanceTypesForGenericType1
TestJavascriptModules20
TestJavascriptModulesTypeImport
TestJsDocAugments
TestJsDocAugmentsAndExtends
TestJsdocCallbackTag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func TestJavascriptModulesTypeImport(t *testing.T) {
t.Parallel()
t.Skip()

defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @allowJs: true
// @Filename: types.js
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect1.ts] ////

//// [typedefModuleExportsIndirect1.js]
/** @typedef {{ a: 1, m: 1 }} C */
const dummy = 0;
module.exports = dummy;
//// [use.js]
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
var c


//// [typedefModuleExportsIndirect1.js]
"use strict";
/** @typedef {{ a: 1, m: 1 }} C */
const dummy = 0;
module.exports = dummy;
//// [use.js]
"use strict";
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
var c;


//// [typedefModuleExportsIndirect1.d.ts]
export type C = {
a: 1;
m: 1;
};
export = dummy;
//// [use.d.ts]
type C = import('./typedefModuleExportsIndirect1').C;
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
declare var c: C;


//// [DtsFileErrors]


dist/typedefModuleExportsIndirect1.d.ts(5,1): error TS2309: An export assignment cannot be used in a module with other exported elements.
dist/typedefModuleExportsIndirect1.d.ts(5,10): error TS2304: Cannot find name 'dummy'.
dist/use.d.ts(1,52): error TS2694: Namespace 'unknown' has no exported member 'C'.


==== dist/typedefModuleExportsIndirect1.d.ts (2 errors) ====
export type C = {
a: 1;
m: 1;
};
export = dummy;
~~~~~~~~~~~~~~~
!!! error TS2309: An export assignment cannot be used in a module with other exported elements.
~~~~~
!!! error TS2304: Cannot find name 'dummy'.

==== dist/use.d.ts (1 errors) ====
type C = import('./typedefModuleExportsIndirect1').C;
~
!!! error TS2694: Namespace 'unknown' has no exported member 'C'.
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
declare var c: C;

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect1.ts] ////

=== typedefModuleExportsIndirect1.js ===
/** @typedef {{ a: 1, m: 1 }} C */
const dummy = 0;
>dummy : Symbol(dummy, Decl(typedefModuleExportsIndirect1.js, 1, 5))

module.exports = dummy;
>module.exports : Symbol(dummy, Decl(typedefModuleExportsIndirect1.js, 1, 5))
>module : Symbol("typedefModuleExportsIndirect1", Decl(typedefModuleExportsIndirect1.js, 0, 0))
>exports : Symbol(dummy, Decl(typedefModuleExportsIndirect1.js, 1, 5))
>dummy : Symbol(dummy, Decl(typedefModuleExportsIndirect1.js, 1, 5))

=== use.js ===
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
var c
>c : Symbol(c, Decl(use.js, 2, 3))

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect1.ts] ////

=== typedefModuleExportsIndirect1.js ===
/** @typedef {{ a: 1, m: 1 }} C */
const dummy = 0;
>dummy : 0
>0 : 0

module.exports = dummy;
>module.exports = dummy : 0
>module.exports : 0
>module : { readonly dummy: 0; }
>exports : 0
>dummy : 0

=== use.js ===
/** @typedef {import('./typedefModuleExportsIndirect1').C} C */
/** @type {C} */
var c
>c : import("typedefModuleExportsIndirect1").C

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect2.ts] ////

//// [typedefModuleExportsIndirect2.js]
/** @typedef {{ a: 1, m: 1 }} C */
const f = function() {};
module.exports = f;
//// [use.js]
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
var c


//// [typedefModuleExportsIndirect2.js]
"use strict";
/** @typedef {{ a: 1, m: 1 }} C */
const f = function () { };
module.exports = f;
//// [use.js]
"use strict";
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
var c;


//// [typedefModuleExportsIndirect2.d.ts]
export type C = {
a: 1;
m: 1;
};
export = f;
//// [use.d.ts]
type C = import('./typedefModuleExportsIndirect2').C;
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
declare var c: C;


//// [DtsFileErrors]


dist/typedefModuleExportsIndirect2.d.ts(5,1): error TS2309: An export assignment cannot be used in a module with other exported elements.
dist/typedefModuleExportsIndirect2.d.ts(5,10): error TS2304: Cannot find name 'f'.
dist/use.d.ts(1,52): error TS2694: Namespace 'unknown' has no exported member 'C'.


==== dist/typedefModuleExportsIndirect2.d.ts (2 errors) ====
export type C = {
a: 1;
m: 1;
};
export = f;
~~~~~~~~~~~
!!! error TS2309: An export assignment cannot be used in a module with other exported elements.
~
!!! error TS2304: Cannot find name 'f'.

==== dist/use.d.ts (1 errors) ====
type C = import('./typedefModuleExportsIndirect2').C;
~
!!! error TS2694: Namespace 'unknown' has no exported member 'C'.
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
declare var c: C;

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect2.ts] ////

=== typedefModuleExportsIndirect2.js ===
/** @typedef {{ a: 1, m: 1 }} C */
const f = function() {};
>f : Symbol(f, Decl(typedefModuleExportsIndirect2.js, 1, 5))

module.exports = f;
>module.exports : Symbol(f, Decl(typedefModuleExportsIndirect2.js, 1, 5))
>module : Symbol("typedefModuleExportsIndirect2", Decl(typedefModuleExportsIndirect2.js, 0, 0))
>exports : Symbol(f, Decl(typedefModuleExportsIndirect2.js, 1, 5))
>f : Symbol(f, Decl(typedefModuleExportsIndirect2.js, 1, 5))

=== use.js ===
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
var c
>c : Symbol(c, Decl(use.js, 2, 3))

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/conformance/salsa/typedefModuleExportsIndirect2.ts] ////

=== typedefModuleExportsIndirect2.js ===
/** @typedef {{ a: 1, m: 1 }} C */
const f = function() {};
>f : () => void
>function() {} : () => void

module.exports = f;
>module.exports = f : () => void
>module.exports : () => void
>module : { readonly f: () => void; }
>exports : () => void
>f : () => void

=== use.js ===
/** @typedef {import('./typedefModuleExportsIndirect2').C} C */
/** @type {C} */
var c
>c : import("typedefModuleExportsIndirect2").C

Loading