Skip to content

Commit 0b38aae

Browse files
committed
Typings Installer initial work
1 parent 47f76b4 commit 0b38aae

23 files changed

+1853
-141
lines changed

cmd/tsgo/lsp.go

+72
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import (
66
"fmt"
77
"io"
88
"os"
9+
"runtime"
910

1011
"github.com/microsoft/typescript-go/internal/bundled"
1112
"github.com/microsoft/typescript-go/internal/core"
1213
"github.com/microsoft/typescript-go/internal/lsp"
1314
"github.com/microsoft/typescript-go/internal/pprof"
15+
"github.com/microsoft/typescript-go/internal/tspath"
1416
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
1517
)
1618

@@ -39,6 +41,7 @@ func runLSP(args []string) int {
3941

4042
fs := bundled.WrapFS(osvfs.FS())
4143
defaultLibraryPath := bundled.LibPath()
44+
typingsLocation := getGlobalTypingsCacheLocation()
4245

4346
s := lsp.NewServer(&lsp.ServerOptions{
4447
In: os.Stdin,
@@ -47,10 +50,79 @@ func runLSP(args []string) int {
4750
Cwd: core.Must(os.Getwd()),
4851
FS: fs,
4952
DefaultLibraryPath: defaultLibraryPath,
53+
TypingsLocation: typingsLocation,
5054
})
5155

5256
if err := s.Run(); err != nil && !errors.Is(err, io.EOF) {
5357
return 1
5458
}
5559
return 0
5660
}
61+
62+
func getGlobalTypingsCacheLocation() string {
63+
switch runtime.GOOS {
64+
case "windows":
65+
{
66+
basePath, err := os.UserCacheDir()
67+
if err != nil {
68+
if basePath, err = os.UserConfigDir(); err != nil {
69+
if basePath, err = os.UserHomeDir(); err != nil {
70+
if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
71+
basePath = userProfile
72+
} else if homeDrive, homePath := os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"); homeDrive != "" && homePath != "" {
73+
basePath = homeDrive + homePath
74+
} else {
75+
basePath = os.TempDir()
76+
}
77+
}
78+
}
79+
}
80+
return tspath.CombinePaths(tspath.CombinePaths(basePath, "Microsoft/TypeScript"), core.VersionMajorMinor)
81+
}
82+
case "openbsd", "freebsd", "netbsd", "darwin", "linux", "android":
83+
{
84+
cacheLocation := getNonWindowsCacheLocation()
85+
return tspath.CombinePaths(tspath.CombinePaths(cacheLocation, "typescript"), core.VersionMajorMinor)
86+
}
87+
default:
88+
panic("unsupported platform: " + runtime.GOOS)
89+
}
90+
}
91+
92+
func getNonWindowsCacheLocation() string {
93+
if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" {
94+
return xdgCacheHome
95+
}
96+
const platformIsDarwin = runtime.GOOS == "darwin"
97+
var usersDir string
98+
if platformIsDarwin {
99+
usersDir = "Users"
100+
} else {
101+
usersDir = "home"
102+
}
103+
homePath, err := os.UserHomeDir()
104+
if err != nil {
105+
if home := os.Getenv("HOME"); home != "" {
106+
homePath = home
107+
} else {
108+
var userName string
109+
if logName := os.Getenv("LOGNAME"); logName != "" {
110+
userName = logName
111+
} else if user := os.Getenv("USER"); user != "" {
112+
userName = user
113+
}
114+
if userName != "" {
115+
homePath = "/" + usersDir + "/" + userName
116+
} else {
117+
homePath = os.TempDir()
118+
}
119+
}
120+
}
121+
var cacheFolder string
122+
if platformIsDarwin {
123+
cacheFolder = "Library/Caches"
124+
} else {
125+
cacheFolder = ".cache"
126+
}
127+
return tspath.CombinePaths(homePath, cacheFolder)
128+
}

internal/api/api.go

+5
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ func (api *API) DefaultLibraryPath() string {
7272
return api.host.DefaultLibraryPath()
7373
}
7474

75+
// TypingsInstaller implements ProjectHost
76+
func (api *API) TypingsInstaller() *project.TypingsInstaller {
77+
return nil
78+
}
79+
7580
// DocumentRegistry implements ProjectHost.
7681
func (api *API) DocumentRegistry() *project.DocumentRegistry {
7782
return api.documentRegistry

internal/compiler/fileloader.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,11 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(file *ast.SourceFile)
381381
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
382382
hasAllowedExtension := false
383383
if p.compilerOptions.ResolveJsonModule.IsTrue() {
384-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsWithJsonFlat)
384+
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsWithJsonFlat)
385385
} else if p.compilerOptions.AllowJs.IsTrue() {
386-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedJSExtensionsFlat) || tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
386+
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedJSExtensionsFlat) || tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsFlat)
387387
} else {
388-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
388+
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsFlat)
389389
}
390390
shouldAddFile := resolution.IsResolved() && hasAllowedExtension
391391
// TODO(ercornel): !!!: other checks on whether or not to add the file

internal/compiler/program.go

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ type ProgramOptions struct {
2727
SingleThreaded bool
2828
ProjectReference []core.ProjectReference
2929
ConfigFileParsingDiagnostics []*ast.Diagnostic
30+
31+
TypingsLocation string
32+
ProjectName string
3033
}
3134

3235
type Program struct {
@@ -135,6 +138,8 @@ func NewProgram(options ProgramOptions) *Program {
135138
}
136139

137140
p.resolver = module.NewResolver(p.host, p.compilerOptions)
141+
p.resolver.TypingsLocation = p.programOptions.TypingsLocation
142+
p.resolver.ProjectName = p.programOptions.ProjectName
138143

139144
var libs []string
140145

internal/core/nodemodules.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package core
2+
3+
import "maps"
4+
5+
var UnprefixedNodeCoreModules = map[string]bool{
6+
"assert": true,
7+
"assert/strict": true,
8+
"async_hooks": true,
9+
"buffer": true,
10+
"child_process": true,
11+
"cluster": true,
12+
"console": true,
13+
"constants": true,
14+
"crypto": true,
15+
"dgram": true,
16+
"diagnostics_channel": true,
17+
"dns": true,
18+
"dns/promises": true,
19+
"domain": true,
20+
"events": true,
21+
"fs": true,
22+
"fs/promises": true,
23+
"http": true,
24+
"http2": true,
25+
"https": true,
26+
"inspector": true,
27+
"inspector/promises": true,
28+
"module": true,
29+
"net": true,
30+
"os": true,
31+
"path": true,
32+
"path/posix": true,
33+
"path/win32": true,
34+
"perf_hooks": true,
35+
"process": true,
36+
"punycode": true,
37+
"querystring": true,
38+
"readline": true,
39+
"readline/promises": true,
40+
"repl": true,
41+
"stream": true,
42+
"stream/consumers": true,
43+
"stream/promises": true,
44+
"stream/web": true,
45+
"string_decoder": true,
46+
"sys": true,
47+
"test/mock_loader": true,
48+
"timers": true,
49+
"timers/promises": true,
50+
"tls": true,
51+
"trace_events": true,
52+
"tty": true,
53+
"url": true,
54+
"util": true,
55+
"util/types": true,
56+
"v8": true,
57+
"vm": true,
58+
"wasi": true,
59+
"worker_threads": true,
60+
"zlib": true,
61+
}
62+
63+
var ExclusivelyPrefixedNodeCoreModules = map[string]bool{
64+
"node:sea": true,
65+
"node:sqlite": true,
66+
"node:test": true,
67+
"node:test/reporters": true,
68+
}
69+
70+
var nodeCoreModules = map[string]bool{}
71+
72+
func ensureNodeCoreModules() {
73+
if len(nodeCoreModules) != 0 {
74+
return
75+
}
76+
for unprefixed := range UnprefixedNodeCoreModules {
77+
nodeCoreModules[unprefixed] = true
78+
nodeCoreModules["node:"+unprefixed] = true
79+
}
80+
maps.Copy(nodeCoreModules, ExclusivelyPrefixedNodeCoreModules)
81+
}
82+
83+
func NonRelativeModuleNameForTypingCache(moduleName string) string {
84+
ensureNodeCoreModules()
85+
if nodeCoreModules[moduleName] {
86+
return "node"
87+
}
88+
return moduleName
89+
}

internal/lsp/server.go

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type ServerOptions struct {
2626
NewLine core.NewLineKind
2727
FS vfs.FS
2828
DefaultLibraryPath string
29+
TypingsLocation string
2930
}
3031

3132
func NewServer(opts *ServerOptions) *Server {
@@ -40,6 +41,7 @@ func NewServer(opts *ServerOptions) *Server {
4041
newLine: opts.NewLine,
4142
fs: opts.FS,
4243
defaultLibraryPath: opts.DefaultLibraryPath,
44+
typingsLocation: opts.TypingsLocation,
4345
}
4446
}
4547

@@ -62,6 +64,7 @@ type Server struct {
6264
newLine core.NewLineKind
6365
fs vfs.FS
6466
defaultLibraryPath string
67+
typingsLocation string
6568

6669
initializeParams *lsproto.InitializeParams
6770
positionEncoding lsproto.PositionEncodingKind
@@ -84,6 +87,11 @@ func (s *Server) DefaultLibraryPath() string {
8487
return s.defaultLibraryPath
8588
}
8689

90+
// TypingsLocation implements project.ServiceHost.
91+
func (s *Server) TypingsLocation() string {
92+
return s.typingsLocation
93+
}
94+
8795
// GetCurrentDirectory implements project.ServiceHost.
8896
func (s *Server) GetCurrentDirectory() string {
8997
return s.cwd

internal/module/resolver.go

+32
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ type Resolver struct {
109109
caches
110110
host ResolutionHost
111111
compilerOptions *core.CompilerOptions
112+
TypingsLocation string
113+
ProjectName string
112114
// reportDiagnostic: DiagnosticReporter
113115
}
114116

@@ -229,6 +231,36 @@ func (r *Resolver) ResolveModuleName(moduleName string, containingFile string, r
229231
}
230232
}
231233

234+
return r.tryResolveFromTypingsLocation(moduleName, containingDirectory, result)
235+
}
236+
237+
func (r *Resolver) tryResolveFromTypingsLocation(moduleName string, containingDirectory string, originalResult *ResolvedModule) *ResolvedModule {
238+
if r.TypingsLocation == "" ||
239+
tspath.IsExternalModuleNameRelative(moduleName) ||
240+
(originalResult.ResolvedFileName != "" && tspath.ExtensionIsOneOf(originalResult.Extension, tspath.SupportedTSExtensionsWithJsonFlat)) {
241+
return originalResult
242+
}
243+
244+
state := newResolutionState(
245+
moduleName,
246+
containingDirectory,
247+
false, /*isTypeReferenceDirective*/
248+
core.ModuleKindNone, // resolutionMode,
249+
r.compilerOptions,
250+
nil, // redirectedReference,
251+
r,
252+
)
253+
if r.traceEnabled() {
254+
r.host.Trace(diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2.Format(r.ProjectName, moduleName, r.TypingsLocation))
255+
}
256+
globalResolved := state.loadModuleFromImmediateNodeModulesDirectory(extensionsDeclaration, r.TypingsLocation, false)
257+
if globalResolved == nil {
258+
return originalResult
259+
}
260+
result := state.createResolvedModule(globalResolved, true)
261+
result.FailedLookupLocations = append(originalResult.FailedLookupLocations, result.FailedLookupLocations...)
262+
result.AffectingLocations = append(originalResult.AffectingLocations, result.AffectingLocations...)
263+
result.ResolutionDiagnostics = append(originalResult.ResolutionDiagnostics, result.ResolutionDiagnostics...)
232264
return result
233265
}
234266

internal/packagejson/packagejson.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type PathFields struct {
2222

2323
type DependencyFields struct {
2424
Dependencies Expected[map[string]string] `json:"dependencies"`
25+
DevDependencies Expected[map[string]string] `json:"devDependencies"`
2526
PeerDependencies Expected[map[string]string] `json:"peerDependencies"`
2627
OptionalDependencies Expected[map[string]string] `json:"optionalDependencies"`
2728
}

0 commit comments

Comments
 (0)