Skip to content

Typings Intaller initial work #844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
72 changes: 72 additions & 0 deletions cmd/tsgo/lsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"fmt"
"io"
"os"
"runtime"

"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/lsp"
"github.com/microsoft/typescript-go/internal/pprof"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
)

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

fs := bundled.WrapFS(osvfs.FS())
defaultLibraryPath := bundled.LibPath()
typingsLocation := getGlobalTypingsCacheLocation()

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

if err := s.Run(); err != nil && !errors.Is(err, io.EOF) {
return 1
}
return 0
}

func getGlobalTypingsCacheLocation() string {
switch runtime.GOOS {
case "windows":
{
basePath, err := os.UserCacheDir()
if err != nil {
if basePath, err = os.UserConfigDir(); err != nil {
if basePath, err = os.UserHomeDir(); err != nil {
if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
basePath = userProfile
} else if homeDrive, homePath := os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"); homeDrive != "" && homePath != "" {
basePath = homeDrive + homePath
} else {
basePath = os.TempDir()
}
}
}
}
return tspath.CombinePaths(tspath.CombinePaths(basePath, "Microsoft/TypeScript"), core.VersionMajorMinor)
}
case "openbsd", "freebsd", "netbsd", "darwin", "linux", "android":
{
cacheLocation := getNonWindowsCacheLocation()
return tspath.CombinePaths(tspath.CombinePaths(cacheLocation, "typescript"), core.VersionMajorMinor)
}
default:
panic("unsupported platform: " + runtime.GOOS)
}
}

func getNonWindowsCacheLocation() string {
if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" {
return xdgCacheHome
}
const platformIsDarwin = runtime.GOOS == "darwin"
var usersDir string
if platformIsDarwin {
usersDir = "Users"
} else {
usersDir = "home"
}
homePath, err := os.UserHomeDir()
if err != nil {
if home := os.Getenv("HOME"); home != "" {
homePath = home
} else {
var userName string
if logName := os.Getenv("LOGNAME"); logName != "" {
userName = logName
} else if user := os.Getenv("USER"); user != "" {
userName = user
}
if userName != "" {
homePath = "/" + usersDir + "/" + userName
} else {
homePath = os.TempDir()
}
}
}
var cacheFolder string
if platformIsDarwin {
cacheFolder = "Library/Caches"
} else {
cacheFolder = ".cache"
}
return tspath.CombinePaths(homePath, cacheFolder)
}
17 changes: 16 additions & 1 deletion internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func (api *API) DefaultLibraryPath() string {
return api.host.DefaultLibraryPath()
}

// TypingsInstaller implements ProjectHost
func (api *API) TypingsInstaller() *project.TypingsInstaller {
return nil
}

// DocumentRegistry implements ProjectHost.
func (api *API) DocumentRegistry() *project.DocumentRegistry {
return api.documentRegistry
Expand Down Expand Up @@ -119,6 +124,16 @@ func (api *API) PositionEncoding() lsproto.PositionEncodingKind {
return lsproto.PositionEncodingKindUTF8
}

// Client implements ProjectHost.
func (api *API) Client() project.Client {
return nil
}

// IsWatchEnabled implements ProjectHost.
func (api *API) IsWatchEnabled() bool {
return false
}

func (api *API) HandleRequest(id int, method string, payload []byte) ([]byte, error) {
params, err := unmarshalPayload(method, payload)
if err != nil {
Expand Down Expand Up @@ -351,7 +366,7 @@ func (api *API) getOrCreateScriptInfo(fileName string, path tspath.Path, scriptK
if !ok {
return nil
}
info = project.NewScriptInfo(fileName, path, scriptKind)
info = project.NewScriptInfo(fileName, path, scriptKind, api.host.FS())
info.SetTextFromDisk(content)
api.scriptInfosMu.Lock()
defer api.scriptInfosMu.Unlock()
Expand Down
6 changes: 3 additions & 3 deletions internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,11 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(file *ast.SourceFile)
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
hasAllowedExtension := false
if p.compilerOptions.ResolveJsonModule.IsTrue() {
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsWithJsonFlat)
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsWithJsonFlat)
} else if p.compilerOptions.AllowJs.IsTrue() {
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedJSExtensionsFlat) || tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedJSExtensionsFlat) || tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsFlat)
} else {
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
hasAllowedExtension = tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsFlat)
}
shouldAddFile := resolution.IsResolved() && hasAllowedExtension
// TODO(ercornel): !!!: other checks on whether or not to add the file
Expand Down
5 changes: 5 additions & 0 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type ProgramOptions struct {
SingleThreaded bool
ProjectReference []core.ProjectReference
ConfigFileParsingDiagnostics []*ast.Diagnostic

TypingsLocation string
ProjectName string
}

type Program struct {
Expand Down Expand Up @@ -135,6 +138,8 @@ func NewProgram(options ProgramOptions) *Program {
}

p.resolver = module.NewResolver(p.host, p.compilerOptions)
p.resolver.TypingsLocation = p.programOptions.TypingsLocation
p.resolver.ProjectName = p.programOptions.ProjectName

var libs []string

Expand Down
89 changes: 89 additions & 0 deletions internal/core/nodemodules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package core

import "maps"

var UnprefixedNodeCoreModules = map[string]bool{
"assert": true,
"assert/strict": true,
"async_hooks": true,
"buffer": true,
"child_process": true,
"cluster": true,
"console": true,
"constants": true,
"crypto": true,
"dgram": true,
"diagnostics_channel": true,
"dns": true,
"dns/promises": true,
"domain": true,
"events": true,
"fs": true,
"fs/promises": true,
"http": true,
"http2": true,
"https": true,
"inspector": true,
"inspector/promises": true,
"module": true,
"net": true,
"os": true,
"path": true,
"path/posix": true,
"path/win32": true,
"perf_hooks": true,
"process": true,
"punycode": true,
"querystring": true,
"readline": true,
"readline/promises": true,
"repl": true,
"stream": true,
"stream/consumers": true,
"stream/promises": true,
"stream/web": true,
"string_decoder": true,
"sys": true,
"test/mock_loader": true,
"timers": true,
"timers/promises": true,
"tls": true,
"trace_events": true,
"tty": true,
"url": true,
"util": true,
"util/types": true,
"v8": true,
"vm": true,
"wasi": true,
"worker_threads": true,
"zlib": true,
}

var ExclusivelyPrefixedNodeCoreModules = map[string]bool{
"node:sea": true,
"node:sqlite": true,
"node:test": true,
"node:test/reporters": true,
}

var nodeCoreModules = map[string]bool{}

func ensureNodeCoreModules() {
if len(nodeCoreModules) != 0 {
return
}
for unprefixed := range UnprefixedNodeCoreModules {
nodeCoreModules[unprefixed] = true
nodeCoreModules["node:"+unprefixed] = true
}
maps.Copy(nodeCoreModules, ExclusivelyPrefixedNodeCoreModules)
}

func NonRelativeModuleNameForTypingCache(moduleName string) string {
ensureNodeCoreModules()
if nodeCoreModules[moduleName] {
return "node"
}
return moduleName
}
21 changes: 20 additions & 1 deletion internal/lsp/lsproto/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ type ID struct {
int int32
}

func NewIDString(str string) *ID {
return &ID{str: str}
}

func (id *ID) MarshalJSON() ([]byte, error) {
if id.str != "" {
return json.Marshal(id.str)
Expand All @@ -43,6 +47,13 @@ func (id *ID) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &id.int)
}

func (id *ID) TryInt() (int32, bool) {
if id == nil || id.str != "" {
return 0, false
}
return id.int, true
}

func (id *ID) MustInt() int32 {
if id.str != "" {
panic("ID is not an integer")
Expand All @@ -54,11 +65,19 @@ func (id *ID) MustInt() int32 {

type RequestMessage struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
ID *ID `json:"id"`
ID *ID `json:"id,omitempty"`
Method Method `json:"method"`
Params any `json:"params"`
}

func NewRequestMessage(method Method, id *ID, params any) *RequestMessage {
return &RequestMessage{
ID: id,
Method: method,
Params: params,
}
}

func (r *RequestMessage) UnmarshalJSON(data []byte) error {
var raw struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
Expand Down
Loading
Loading