Skip to content

Commit c320d26

Browse files
committed
Merge remote-tracking branch 'upstream/main' into watch-cli-efficiency
2 parents 6f793ed + 9c19dee commit c320d26

File tree

598 files changed

+2898
-3436
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

598 files changed

+2898
-3436
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ This is still a work in progress and is not yet at full feature parity with Type
3434
| Type checking | done | Same errors, locations, and messages as TS 6.0. Types printback in errors may display differently. |
3535
| JavaScript-specific inference and JSDoc | in progress | Mostly complete, but intentionally lacking some features. Declaration emit not complete. |
3636
| JSX | done | - |
37-
| Declaration emit | in progress | Most common features are in place, but some edge cases and feature flags are still unhandled. |
38-
| Emit (JS output) | in progress | `target: esnext` well-supported, other targets may have gaps. |
37+
| Declaration emit | in progress | Done for TypeScript files. Not yet complete for JavaScript files. |
38+
| Emit (JS output) | done | - |
3939
| Watch mode | prototype | Watches files and rebuilds, but no incremental rechecking. Not optimized. |
4040
| Build mode / project references | done | - |
4141
| Incremental build | done | - |
42-
| Language service (LSP) | in progress | Most functionality. More features coming soon. |
42+
| Language service (LSP) | in progress | Nearly all features implemented. |
4343
| API | not ready | - |
4444

4545
Definitions:

internal/ast/diagnostic.go

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@ import (
1111
"github.com/microsoft/typescript-go/internal/locale"
1212
)
1313

14+
// RepopulateDiagnosticKind indicates the kind of repopulation for a diagnostic chain entry.
15+
type RepopulateDiagnosticKind int
16+
17+
const (
18+
RepopulateModeMismatch RepopulateDiagnosticKind = 1
19+
RepopulateModuleNotFound RepopulateDiagnosticKind = 2
20+
)
21+
22+
// RepopulateDiagnosticInfo stores information needed to recompute a diagnostic chain entry
23+
// during incremental builds when the program state may have changed.
24+
type RepopulateDiagnosticInfo struct {
25+
Kind RepopulateDiagnosticKind
26+
ModuleReference string
27+
Mode core.ResolutionMode
28+
PackageName string
29+
}
30+
1431
// Diagnostic
1532

1633
type Diagnostic struct {
@@ -27,27 +44,30 @@ type Diagnostic struct {
2744
reportsUnnecessary bool
2845
reportsDeprecated bool
2946
skippedOnNoEmit bool
47+
repopulateInfo *RepopulateDiagnosticInfo
3048
}
3149

32-
func (d *Diagnostic) File() *SourceFile { return d.file }
33-
func (d *Diagnostic) Pos() int { return d.loc.Pos() }
34-
func (d *Diagnostic) End() int { return d.loc.End() }
35-
func (d *Diagnostic) Len() int { return d.loc.Len() }
36-
func (d *Diagnostic) Loc() core.TextRange { return d.loc }
37-
func (d *Diagnostic) Code() int32 { return d.code }
38-
func (d *Diagnostic) Category() diagnostics.Category { return d.category }
39-
func (d *Diagnostic) MessageKey() diagnostics.Key { return d.messageKey }
40-
func (d *Diagnostic) MessageArgs() []string { return d.messageArgs }
41-
func (d *Diagnostic) MessageChain() []*Diagnostic { return d.messageChain }
42-
func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation }
43-
func (d *Diagnostic) ReportsUnnecessary() bool { return d.reportsUnnecessary }
44-
func (d *Diagnostic) ReportsDeprecated() bool { return d.reportsDeprecated }
45-
func (d *Diagnostic) SkippedOnNoEmit() bool { return d.skippedOnNoEmit }
46-
47-
func (d *Diagnostic) SetFile(file *SourceFile) { d.file = file }
48-
func (d *Diagnostic) SetLocation(loc core.TextRange) { d.loc = loc }
49-
func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category }
50-
func (d *Diagnostic) SetSkippedOnNoEmit() { d.skippedOnNoEmit = true }
50+
func (d *Diagnostic) File() *SourceFile { return d.file }
51+
func (d *Diagnostic) Pos() int { return d.loc.Pos() }
52+
func (d *Diagnostic) End() int { return d.loc.End() }
53+
func (d *Diagnostic) Len() int { return d.loc.Len() }
54+
func (d *Diagnostic) Loc() core.TextRange { return d.loc }
55+
func (d *Diagnostic) Code() int32 { return d.code }
56+
func (d *Diagnostic) Category() diagnostics.Category { return d.category }
57+
func (d *Diagnostic) MessageKey() diagnostics.Key { return d.messageKey }
58+
func (d *Diagnostic) MessageArgs() []string { return d.messageArgs }
59+
func (d *Diagnostic) MessageChain() []*Diagnostic { return d.messageChain }
60+
func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation }
61+
func (d *Diagnostic) ReportsUnnecessary() bool { return d.reportsUnnecessary }
62+
func (d *Diagnostic) ReportsDeprecated() bool { return d.reportsDeprecated }
63+
func (d *Diagnostic) SkippedOnNoEmit() bool { return d.skippedOnNoEmit }
64+
func (d *Diagnostic) RepopulateInfo() *RepopulateDiagnosticInfo { return d.repopulateInfo }
65+
66+
func (d *Diagnostic) SetFile(file *SourceFile) { d.file = file }
67+
func (d *Diagnostic) SetLocation(loc core.TextRange) { d.loc = loc }
68+
func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category }
69+
func (d *Diagnostic) SetSkippedOnNoEmit() { d.skippedOnNoEmit = true }
70+
func (d *Diagnostic) SetRepopulateInfo(info *RepopulateDiagnosticInfo) { d.repopulateInfo = info }
5171

5272
func (d *Diagnostic) SetMessageChain(messageChain []*Diagnostic) *Diagnostic {
5373
d.messageChain = messageChain

internal/binder/nameresolver.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,6 @@ loop:
313313
lastLocation.Kind == ast.KindSourceFile &&
314314
lastLocation.AsSourceFile().CommonJSModuleIndicator != nil &&
315315
name == "module" &&
316-
originalLocation.Parent != nil &&
317-
(ast.IsModuleExportsAccessExpression(originalLocation.Parent) || ast.IsModuleExportsQualifiedName(originalLocation.Parent)) &&
318316
meaning&lastLocation.Symbol().Flags != 0 {
319317
return r.GetModuleSymbol(lastLocation)
320318
}

internal/checker/checker.go

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ type Program interface {
554554
GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind
555555
GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
556556
GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule]
557+
GetPackagesMap() map[string]bool
557558
GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData
558559
GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node)
559560
GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node
@@ -1462,8 +1463,16 @@ func (c *Checker) getModuleSymbol(sourceFile *ast.Node) *ast.Symbol {
14621463
if cached, ok := c.moduleSymbols[sourceFile]; ok {
14631464
return cached
14641465
}
1465-
result := c.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsFunctionScopedVariable, ast.InternalSymbolNameModuleExports)
1466+
result := c.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsFunctionScopedVariable, "module")
1467+
result.Declarations = []*ast.Node{sourceFile}
14661468
result.ValueDeclaration = sourceFile
1469+
exportsSymbol := c.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsProperty, "exports")
1470+
exportsSymbol.Declarations = result.Declarations
1471+
exportsSymbol.ValueDeclaration = result.ValueDeclaration
1472+
exportsSymbol.Parent = result
1473+
members := make(ast.SymbolTable, 1)
1474+
members["exports"] = exportsSymbol
1475+
c.valueSymbolLinks.Get(result).resolvedType = c.newAnonymousType(result, members, nil, nil, nil)
14671476
c.moduleSymbols[sourceFile] = result
14681477
return result
14691478
}
@@ -15011,39 +15020,29 @@ func (c *Checker) errorOnImplicitAnyModule(isError bool, errorNode *ast.Node, mo
1501115020
}
1501215021

1501315022
func (c *Checker) createModuleNotFoundChain(resolvedModule *module.ResolvedModule, errorNode *ast.Node, moduleReference string, mode core.ResolutionMode, packageName string) *ast.Diagnostic {
15014-
if resolvedModule.AlternateResult != "" {
15015-
if strings.Contains(resolvedModule.AlternateResult, "/node_modules/@types/") {
15016-
packageName = "@types/" + module.MangleScopedPackageName(packageName)
15017-
}
15018-
return NewDiagnosticForNode(errorNode, diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings, resolvedModule.AlternateResult, packageName)
15019-
}
15020-
if c.typesPackageExists(packageName) {
15021-
return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, packageName, module.MangleScopedPackageName(packageName))
15022-
}
15023-
if c.packageBundlesTypes(packageName) {
15024-
return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1, packageName, moduleReference)
15025-
}
15026-
return NewDiagnosticForNode(errorNode, diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, moduleReference, module.MangleScopedPackageName(packageName))
15023+
// Store the original packageName for repopulateInfo before any modifications
15024+
storedPackageName := packageName
15025+
if storedPackageName == moduleReference {
15026+
storedPackageName = ""
15027+
}
15028+
15029+
details := CreateModuleNotFoundChain(c.program, ast.GetSourceFileOfNode(errorNode), moduleReference, mode, packageName)
15030+
result := NewDiagnosticForNode(errorNode, details.Message, details.Args...)
15031+
result.SetRepopulateInfo(&ast.RepopulateDiagnosticInfo{
15032+
Kind: ast.RepopulateModuleNotFound,
15033+
ModuleReference: moduleReference,
15034+
Mode: mode,
15035+
PackageName: storedPackageName,
15036+
})
15037+
return result
1502715038
}
1502815039

1502915040
func (c *Checker) createModeMismatchDetails(sourceFile *ast.SourceFile, errorNode *ast.Node) *ast.Diagnostic {
15030-
ext := tspath.TryGetExtensionFromPath(sourceFile.FileName())
15031-
targetExt := core.IfElse(ext == tspath.ExtensionTs, tspath.ExtensionMts, core.IfElse(ext == tspath.ExtensionJs, tspath.ExtensionMjs, ""))
15032-
meta := c.program.GetSourceFileMetaData(sourceFile.Path())
15033-
packageJsonType := meta.PackageJsonType
15034-
packageJsonDirectory := meta.PackageJsonDirectory
15035-
var result *ast.Diagnostic
15036-
if packageJsonDirectory != "" && packageJsonType == "" {
15037-
if targetExt != "" {
15038-
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1, targetExt, tspath.CombinePaths(packageJsonDirectory, "package.json"))
15039-
} else {
15040-
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0, tspath.CombinePaths(packageJsonDirectory, "package.json"))
15041-
}
15042-
} else if targetExt != "" {
15043-
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module, targetExt)
15044-
} else {
15045-
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module)
15046-
}
15041+
details := CreateModeMismatchDetails(c.program, sourceFile)
15042+
result := NewDiagnosticForNode(errorNode, details.Message, details.Args...)
15043+
result.SetRepopulateInfo(&ast.RepopulateDiagnosticInfo{
15044+
Kind: ast.RepopulateModeMismatch,
15045+
})
1504715046
return result
1504815047
}
1504915048

@@ -16081,12 +16080,6 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo
1608116080
if symbol == c.requireSymbol {
1608216081
return c.anyType
1608316082
}
16084-
if symbol.Flags&ast.SymbolFlagsModuleExports != 0 && symbol.ValueDeclaration != nil {
16085-
fileSymbol := c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/)
16086-
members := make(ast.SymbolTable, 1)
16087-
members["exports"] = fileSymbol
16088-
return c.newAnonymousType(symbol, members, nil, nil, nil)
16089-
}
1609016083
debug.Assert(symbol.ValueDeclaration != nil)
1609116084
declaration := symbol.ValueDeclaration
1609216085
if ast.IsSourceFile(declaration) && ast.IsJsonSourceFile(declaration.AsSourceFile()) {
@@ -16100,6 +16093,9 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo
1610016093
if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) {
1610116094
return c.reportCircularityError(symbol)
1610216095
}
16096+
if symbol.Flags&ast.SymbolFlagsModuleExports != 0 {
16097+
return c.getTypeOfSymbol(c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/))
16098+
}
1610316099
var result *Type
1610416100
switch declaration.Kind {
1610516101
case ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindVariableDeclaration,
@@ -30890,9 +30886,6 @@ func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast
3089030886
}
3089130887
meaning := core.IfElse(isJSDoc, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, ast.SymbolFlagsValue)
3089230888
result := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/)
30893-
if result != nil && result.Flags&ast.SymbolFlagsModuleExports != 0 {
30894-
result = result.ValueDeclaration.Symbol() // Symbol of the module source file
30895-
}
3089630889
if result == nil && isJSDoc {
3089730890
if container := ast.FindAncestor(name, ast.IsClassOrInterfaceLike); container != nil {
3089830891
symbol := c.getSymbolOfDeclaration(container)

internal/checker/utilities.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,3 +1732,81 @@ func (c *Checker) isJSLiteralType(t *Type) bool {
17321732
}
17331733
return false
17341734
}
1735+
1736+
// DiagnosticDetails holds a resolved diagnostic message and its arguments,
1737+
// used for sharing diagnostic chain computation between the checker and incremental builder.
1738+
type DiagnosticDetails struct {
1739+
Message *diagnostics.Message
1740+
Args []any
1741+
}
1742+
1743+
// CreateModuleNotFoundChain computes the diagnostic message and arguments for a module-not-found
1744+
// error chain entry. This is shared between the checker (initial diagnostic creation) and the
1745+
// incremental builder (repopulation of cached diagnostics).
1746+
// Mirrors createModuleNotFoundChain in the TypeScript compiler's utilities.ts.
1747+
func CreateModuleNotFoundChain(program Program, file *ast.SourceFile, moduleReference string, mode core.ResolutionMode, packageName string) DiagnosticDetails {
1748+
resolvedModule := program.GetResolvedModule(file, moduleReference, mode)
1749+
1750+
if resolvedModule != nil && resolvedModule.AlternateResult != "" {
1751+
if strings.Contains(resolvedModule.AlternateResult, "/node_modules/@types/") {
1752+
packageName = "@types/" + module.MangleScopedPackageName(packageName)
1753+
}
1754+
return DiagnosticDetails{
1755+
Message: diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings,
1756+
Args: []any{resolvedModule.AlternateResult, packageName},
1757+
}
1758+
}
1759+
1760+
packagesMap := program.GetPackagesMap()
1761+
if _, ok := packagesMap[module.GetTypesPackageName(packageName)]; ok {
1762+
return DiagnosticDetails{
1763+
Message: diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1,
1764+
Args: []any{packageName, module.MangleScopedPackageName(packageName)},
1765+
}
1766+
}
1767+
if packagesMap[packageName] {
1768+
return DiagnosticDetails{
1769+
Message: diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1,
1770+
Args: []any{packageName, moduleReference},
1771+
}
1772+
}
1773+
return DiagnosticDetails{
1774+
Message: diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
1775+
Args: []any{moduleReference, module.MangleScopedPackageName(packageName)},
1776+
}
1777+
}
1778+
1779+
// CreateModeMismatchDetails computes the diagnostic message and arguments for a mode-mismatch
1780+
// error chain entry. This is shared between the checker (initial diagnostic creation) and the
1781+
// incremental builder (repopulation of cached diagnostics).
1782+
// Mirrors createModeMismatchDetails in the TypeScript compiler's utilities.ts.
1783+
func CreateModeMismatchDetails(program Program, file *ast.SourceFile) DiagnosticDetails {
1784+
ext := tspath.TryGetExtensionFromPath(file.FileName())
1785+
targetExt := core.IfElse(ext == tspath.ExtensionTs, tspath.ExtensionMts, core.IfElse(ext == tspath.ExtensionJs, tspath.ExtensionMjs, ""))
1786+
meta := program.GetSourceFileMetaData(file.Path())
1787+
packageJsonType := meta.PackageJsonType
1788+
packageJsonDirectory := meta.PackageJsonDirectory
1789+
1790+
if packageJsonDirectory != "" && packageJsonType == "" {
1791+
if targetExt != "" {
1792+
return DiagnosticDetails{
1793+
Message: diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1,
1794+
Args: []any{targetExt, tspath.CombinePaths(packageJsonDirectory, "package.json")},
1795+
}
1796+
}
1797+
return DiagnosticDetails{
1798+
Message: diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0,
1799+
Args: []any{tspath.CombinePaths(packageJsonDirectory, "package.json")},
1800+
}
1801+
}
1802+
if targetExt != "" {
1803+
return DiagnosticDetails{
1804+
Message: diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module,
1805+
Args: []any{targetExt},
1806+
}
1807+
}
1808+
return DiagnosticDetails{
1809+
Message: diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module,
1810+
Args: nil,
1811+
}
1812+
}

internal/compiler/program.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ type Program struct {
109109
// Used by workspace/symbol
110110
hasTSFileOnce sync.Once
111111
hasTSFile bool
112+
113+
// Cached map of package names to whether they bundle types
114+
packagesMapOnce sync.Once
115+
packagesMap map[string]bool
112116
}
113117

114118
// FileExists implements checker.Program.
@@ -467,6 +471,22 @@ func (p *Program) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*mo
467471
return p.resolvedModules
468472
}
469473

474+
// GetPackagesMap returns a lazily-cached map of package names to whether they bundle types.
475+
// This is used by incremental diagnostic repopulation.
476+
func (p *Program) GetPackagesMap() map[string]bool {
477+
p.packagesMapOnce.Do(func() {
478+
p.packagesMap = make(map[string]bool)
479+
for _, resolvedModulesInFile := range p.resolvedModules {
480+
for _, mod := range resolvedModulesInFile {
481+
if mod.PackageId.Name != "" {
482+
p.packagesMap[mod.PackageId.Name] = p.packagesMap[mod.PackageId.Name] || mod.Extension == tspath.ExtensionDts
483+
}
484+
}
485+
}
486+
})
487+
return p.packagesMap
488+
}
489+
470490
// collectDiagnostics collects diagnostics from a single file or all files.
471491
// If sourceFile is non-nil, returns diagnostics for just that file.
472492
// If sourceFile is nil, returns diagnostics for all files in the program.

0 commit comments

Comments
 (0)