Skip to content

Commit a2eb8af

Browse files
author
extern.shiji.yan
committed
refactor: address PR review — auto-install tsgo via npm, merge tests into existing suite
- Replace shutil.which() lookup with npm-based auto-install pattern (using RuntimeDependencyCollection like typescript_language_server) - Remove standalone test_typescript_tsgo.py; parametrize existing typescript tests with TYPESCRIPT_TSGO (always in CI via is_ci) - Add tsgo installation step to pytest CI workflow - Update docs to reflect automatic installation
1 parent 46a2c53 commit a2eb8af

6 files changed

Lines changed: 74 additions & 90 deletions

File tree

.github/workflows/pytest.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,10 @@ jobs:
399399
uses: krdlab/setup-haxe@v2
400400
with:
401401
haxe-version: 4.3.7
402+
- name: Install tsgo (TypeScript native LSP)
403+
shell: bash
404+
run: npm install -g @typescript/native-preview
405+
402406
- name: Install Elm
403407
shell: bash
404408
run: npm install -g elm@0.19.1-6

docs/01-about/020_programming-languages.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Some languages require additional installations or setup steps, as noted.
113113
* **Swift**
114114
* **TypeScript**
115115
(by default, uses [typescript-language-server](https://github.com/typescript-language-server/typescript-language-server) (language `typescript`);
116-
we also support [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — does not require Node.js, must be installed separately via `npm install -g @typescript/native-preview`)
116+
alternatively supports [tsgo](https://github.com/nicolo-ribaudo/tc39-proposal-type-annotations) (language `typescript_tsgo`), the native Go-based TypeScript 7 compiler with built-in LSP — automatically installed via npm)
117117
* **Vue**
118118
(3.x with TypeScript; requires Node.js v18+ and npm; supports .vue Single File Components with monorepo detection)
119119
* **YAML**

src/solidlsp/language_servers/tsgo_language_server.py

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@
1010
import logging
1111
import os
1212
import pathlib
13-
import subprocess
13+
import shutil
1414
from typing import cast
1515

1616
from overrides import override
17+
from sensai.util.logging import LogTime
1718

1819
from solidlsp.ls import LanguageServerDependencyProviderSinglePath, SolidLanguageServer
1920
from solidlsp.ls_config import LanguageServerConfig
2021
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
2122
from solidlsp.settings import SolidLSPSettings
2223

24+
from .common import RuntimeDependency, RuntimeDependencyCollection, build_npm_install_command
2325
from .typescript_language_server import prefer_non_node_modules_definition
2426

2527
log = logging.getLogger(__name__)
@@ -29,11 +31,11 @@ class TsgoLanguageServer(SolidLanguageServer):
2931
"""
3032
TypeScript support via tsgo — the native Go-based TypeScript 7 compiler with built-in LSP.
3133
32-
tsgo does not require Node.js. It must be installed separately
33-
(e.g. via ``npm install -g @typescript/native-preview``).
34+
tsgo is installed automatically via npm (``@typescript/native-preview``).
35+
It does not require the traditional typescript-language-server wrapper.
3436
3537
You can pass the following entries in ls_specific_settings["typescript_tsgo"]:
36-
- ls_path: Path to the tsgo binary (default: discovered from PATH)
38+
- tsgo_version: Version of @typescript/native-preview to install (default: "7.0.0-dev.20250601")
3739
"""
3840

3941
@override
@@ -49,29 +51,58 @@ def is_ignored_dirname(self, dirname: str) -> bool:
4951

5052
class DependencyProvider(LanguageServerDependencyProviderSinglePath):
5153
def _get_or_install_core_dependency(self) -> str:
52-
"""Locate the tsgo binary on the system. tsgo must be pre-installed by the user."""
53-
import shutil
54-
55-
tsgo_path = shutil.which("tsgo")
56-
if tsgo_path is None:
57-
raise RuntimeError(
58-
"tsgo is not installed or not found in PATH.\n"
59-
"Install it via: npm install -g @typescript/native-preview\n"
60-
"Or set the 'ls_path' option in ls_specific_settings['typescript_tsgo'] "
61-
"to point to your tsgo binary."
62-
)
63-
64-
# Verify it works
65-
try:
66-
result = subprocess.run([tsgo_path, "--version"], capture_output=True, text=True, check=False, timeout=10)
67-
if result.returncode == 0:
68-
log.info(f"Found tsgo: {result.stdout.strip()}")
69-
else:
70-
log.warning(f"tsgo --version returned non-zero exit code: {result.returncode}")
71-
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
72-
raise RuntimeError(f"Failed to verify tsgo binary at {tsgo_path}: {e}") from e
73-
74-
return tsgo_path
54+
"""Install @typescript/native-preview via npm and return the path to the tsgo executable."""
55+
language_specific_config = self._custom_settings
56+
tsgo_version = language_specific_config.get("tsgo_version", "7.0.0-dev.20250601")
57+
npm_registry = language_specific_config.get("npm_registry")
58+
59+
deps = RuntimeDependencyCollection(
60+
[
61+
RuntimeDependency(
62+
id="typescript-native-preview",
63+
description="@typescript/native-preview (tsgo) package",
64+
command=build_npm_install_command("@typescript/native-preview", tsgo_version, npm_registry),
65+
platform_id="any",
66+
),
67+
]
68+
)
69+
70+
# Verify npm is installed
71+
is_npm_installed = shutil.which("npm") is not None
72+
assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again."
73+
74+
tsgo_ls_dir = os.path.join(self._ls_resources_dir, "tsgo-lsp")
75+
tsgo_executable_path = os.path.join(tsgo_ls_dir, "node_modules", ".bin", "tsgo")
76+
77+
# Check if installation is needed
78+
version_file = os.path.join(tsgo_ls_dir, ".installed_version")
79+
expected_version = tsgo_version
80+
81+
needs_install = False
82+
if not os.path.exists(tsgo_executable_path):
83+
log.info(f"tsgo executable not found at {tsgo_executable_path}.")
84+
needs_install = True
85+
elif os.path.exists(version_file):
86+
with open(version_file) as f:
87+
installed_version = f.read().strip()
88+
if installed_version != expected_version:
89+
log.info(f"tsgo version mismatch: installed={installed_version}, expected={expected_version}. Reinstalling...")
90+
needs_install = True
91+
else:
92+
log.info("tsgo version file not found. Reinstalling to ensure correct version...")
93+
needs_install = True
94+
95+
if needs_install:
96+
log.info("Installing tsgo dependencies...")
97+
with LogTime("Installation of tsgo (typescript/native-preview)", logger=log):
98+
deps.install(tsgo_ls_dir)
99+
with open(version_file, "w") as f:
100+
f.write(expected_version)
101+
log.info("tsgo installed successfully")
102+
103+
if not os.path.exists(tsgo_executable_path):
104+
raise FileNotFoundError(f"tsgo executable not found at {tsgo_executable_path}, something went wrong with the installation.")
105+
return tsgo_executable_path
75106

76107
def _create_launch_command(self, core_path: str) -> list[str]:
77108
return [core_path, "--lsp", "--stdio"]

test/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def project_with_ls(request: LanguageParamRequest) -> Iterator[Project]:
269269
Language.PYTHON_TY: [pytest.mark.python],
270270
Language.RUST: [pytest.mark.rust],
271271
Language.TYPESCRIPT: [pytest.mark.typescript],
272+
Language.TYPESCRIPT_TSGO: [pytest.mark.typescript],
272273
}
273274

274275

test/solidlsp/typescript/test_typescript_basic.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,26 @@
55
from solidlsp import SolidLanguageServer
66
from solidlsp.ls_config import Language
77
from solidlsp.ls_utils import SymbolUtils
8+
from test.conftest import is_ci, language_tests_enabled
89
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
910

11+
_typescript_servers: list[Language] = [Language.TYPESCRIPT]
12+
13+
# tsgo is always tested in CI; locally only if the language is enabled
14+
if is_ci or language_tests_enabled(Language.TYPESCRIPT_TSGO):
15+
_typescript_servers.append(Language.TYPESCRIPT_TSGO)
16+
1017

1118
@pytest.mark.typescript
1219
class TestTypescriptLanguageServer:
13-
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
20+
@pytest.mark.parametrize("language_server", _typescript_servers, indirect=True)
1421
def test_find_symbol(self, language_server: SolidLanguageServer) -> None:
1522
symbols = language_server.request_full_symbol_tree()
1623
assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree"
1724
assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree"
1825
assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree"
1926

20-
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
27+
@pytest.mark.parametrize("language_server", _typescript_servers, indirect=True)
2128
def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None:
2229
file_path = os.path.join("index.ts")
2330
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
@@ -33,7 +40,7 @@ def test_find_referencing_symbols(self, language_server: SolidLanguageServer) ->
3340
"index.ts should reference helperFunction (tried all positions in selectionRange)"
3441
)
3542

36-
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
43+
@pytest.mark.parametrize("language_server", _typescript_servers, indirect=True)
3744
def test_bare_symbol_names(self, language_server) -> None:
3845
all_symbols = request_all_symbols(language_server)
3946
malformed_symbols = []

test/solidlsp/typescript/test_typescript_tsgo.py

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)