Add Astro language server support#1669
Conversation
Add support for the Astro web framework via @astrojs/language-server with a companion TypeScript language server (configured with @astrojs/ts-plugin) for cross-file code intelligence. The Astro LS handles .astro parsing and document symbols; the companion TypeScript server handles go-to-definition, references and rename for .ts/.js files and cross-file resolution into .astro files. The adapter mirrors the Vue and Svelte language servers, which keep their companion server in a self-contained `self._ts_server` attribute rather than a shared companion base class. Details: - @astrojs/ts-plugin is installed explicitly (it is not a dependency of @astrojs/language-server) and its path is validated at startup; without it the companion has no .astro awareness and cross-file resolution silently fails. - .tsx/.jsx are opened as typescriptreact/javascriptreact to avoid tsserver truncating symbol ranges at JSX expressions. - If the Astro LS fails to start after the companion is up, the companion and the partial Astro process are torn down before re-raising (as Angular does). - Registration mirrors Vue/Svelte: Language.ASTRO enum, superset priority, a .astro source-file matcher, and get_ls_class wiring. Adds the .astro source extension to the hooks extension set and an `astro` pytest marker (astro tests run in the CI catch-all batch, which already provides Node.js). - Documentation: README language list, docs/01-about/020_programming-languages.md, and CHANGELOG (under the existing "Language Servers" section). Tests assert concrete symbol names and exact reference locations, including a cross-file reference from counter.ts into index.astro. A session-scoped conftest installs the fixture's npm dependencies (mirroring Svelte/Angular) so the tests do not pass vacuously. This builds on the Astro work in dansasser/serena PR oraios#886. That PR was written against a companion-server base class abstraction that is not present in upstream main, so the adapter here was implemented against the Vue/Svelte companion pattern currently in main instead. Co-authored-by: dansasser <sasserenterprises@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
CI triage: the |
MischaPanch
left a comment
There was a problem hiding this comment.
Thank you for the contribution!
We never had time to properly review the companion server refactoring, so I agree that what you did is the reasonable step for adding Astro support. Overall looks good, only minor comments. Please have a look at the tests and remove the repo_path fixture if possible.
Also, please add the astro marker to the batch with other web frameworks in the pytest.yml github workflow (comment on top of the file explains everything)
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| REPO_ROOT = Path(__file__).resolve().parents[2] / "resources" / "repos" / "astro" / "test_repo" |
There was a problem hiding this comment.
pls use get_repo_path from conftest
| INSTALL_LOCK = REPO_ROOT / ".astro-install.lock" | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session", autouse=True) |
There was a problem hiding this comment.
since autouse is true, this should be marked with pytest.mark.astro, though I am not sure whether pytest fixtures support that
|
|
||
| @pytest.mark.parametrize("language_server", [Language.ASTRO], indirect=True) | ||
| @pytest.mark.parametrize("repo_path", [Language.ASTRO], indirect=True) | ||
| def test_astro_document_symbols(self, language_server: SolidLanguageServer, repo_path: Path) -> None: |
There was a problem hiding this comment.
something is strange about these tests. We just need relative paths, so there's no need to use repo_path. No other tests use it, why did you have to do it here?
| - Add support for **Astro** (`@astrojs/language-server`) with a companion `typescript-language-server` | ||
| (configured with `@astrojs/ts-plugin`) for cross-file go-to-definition, references and rename between | ||
| `.ts`/`.js` and `.astro` files. Requires Node.js v18+ and npm; dependencies auto-install on first use. | ||
| Use language `astro` instead of also enabling `typescript`. |
There was a problem hiding this comment.
| - Add support for **Astro** (`@astrojs/language-server`) with a companion `typescript-language-server` | |
| (configured with `@astrojs/ts-plugin`) for cross-file go-to-definition, references and rename between | |
| `.ts`/`.js` and `.astro` files. Requires Node.js v18+ and npm; dependencies auto-install on first use. | |
| Use language `astro` instead of also enabling `typescript`. | |
| - Add support for **Astro** |
|
|
||
| # Get TypeScript version settings from TypeScript language server settings | ||
| typescript_config = solidlsp_settings.get_ls_specific_settings(Language.TYPESCRIPT) | ||
| typescript_version = typescript_config.get("typescript_version", "5.9.3") |
There was a problem hiding this comment.
pls put the default versions of everything as constants at the top of the file
Adds support for the Astro web framework using
@astrojs/language-serverwith a companion TypeScript language server forcross-file type resolution.
Relationship to #886 / #884
This builds on @dansasser's Astro work in #886. #884 was closed with the
suggestion to base Astro on the companion-server abstraction from #829, and
#886 was written against that abstraction (
CompanionLanguageServerincompanion_ls.py, plusembedded_language_config.pyandtypescript_companion.py).That abstraction is not present in
main. The Vue and Svelte language serversin the current tree each keep their companion TypeScript server in a
self-contained
self._ts_serverattribute instead of a shared base class. Tomatch what is actually in
main, the adapter here was re-implemented againstthat Vue/Svelte pattern rather than importing the abstraction from #886. As a
result this PR does not add the
CompanionLanguageServerbase class, does notmodify the Vue language server, and is smaller than #886.
Architecture
Dual-server setup, identical in shape to Vue/Svelte:
@astrojs/language-server, run viaastro-ls --stdio) handles.astroparsing and document symbols.typescript-language-serverconfigured with the@astrojs/ts-plugininitialization plugin) handles go-to-definition,find-references and rename for
.ts/.jsfiles and cross-file resolution..astrofiles are indexed on the companion so cross-file references resolve.@astrojs/ts-pluginis installed explicitly (it is not a dependency of@astrojs/language-server), and its path is validated at startup with a clearwarning if missing.
AstroLanguageServersubclassesSolidLanguageServer; the nestedAstroTypeScriptServersubclassesTypeScriptLanguageServerand reuses itsDependencyProvider,$/progressindexing tracking andprefer_non_node_modules_definitionhelper. Initialization uses_create_base_initialize_params(server-specific keys only; the common keys aresupplied by the central
InitializeParamsBuilder).Changes
src/solidlsp/language_servers/astro_language_server.py(new)src/solidlsp/ls_config.py:Language.ASTRO, superset priority,.astrosource matcher, factory wiring
src/serena/hooks.py:.astroadded to the source-extension setpyproject.toml:astropytest markerREADME.md,docs/01-about/020_programming-languages.md,CHANGELOG.mdtest/solidlsp/astro/(new):conftest.py,test_astro_basic.py,test_astro_symbol_retrieval.pytest/resources/repos/astro/test_repo/(new): minimal Astro projectRuntime dependencies (
@astrojs/language-server,@astrojs/ts-plugin,typescript,typescript-language-server) are auto-installed via npm intoSerena's managed resource dir on first use, with a version marker file that
triggers reinstall on version change. Defaults:
@astrojs/language-server2.16.11 and
@astrojs/ts-plugin1.10.10 (latest at time of writing), bothoverridable via
ls_specific_settings.astro.The test fixture's own npm dependencies (
astro, needed by the ts-plugin toresolve
.astromodules) are installed bytest/solidlsp/astro/conftest.py,following the Svelte fixture: a session-scoped, filelock-guarded
npm installinto the fixture's
node_modules, cached across runs and skipped if npm isunavailable. As with Svelte,
package-lock.jsonis git-ignored rather thancommitted.
Tests
Tests assert concrete symbol names and exact reference locations
(
file:line), per the CONTRIBUTING guideline, not just non-empty results. Inparticular, references to
createCounter(defined incounter.ts) are assertedto include its import at
index.astro:4and its call atindex.astro:7— thisexercises the dual-server cross-file path and would fail if
@astrojs/ts-pluginwere missing from the companion. The tests are skipped only when npm is
unavailable (or the fixture install fails), matching the Svelte/Angular
fixtures. The
astromarker is not in any named CI marker group, so the testsrun in the
catch-allpytest batch, which already sets up Node.js on all OSes.Verified locally on Windows (Python 3.14, Node 22):
(13 Astro tests + 42 Vue tests; Vue is included to show it is unchanged.)
To confirm the fixture install path and that nothing passes vacuously, the
Astro suite was also run after deleting both the fixture
node_modulesand themanaged language-server install:
uv run serena --helpstarts cleanly.ruff checkandruff format --checkpass on the changed files.
Notes
have none); the language is documented in
docs/01-about/020_programming-languages.md.request_referencesfor.ts/.jssymbols is served by the companion only.The sharpened cross-file test confirms this already returns references located
inside
.astrofiles, so no separate merge of primary-Astro-LS references isneeded for symbol references. File-level component references (e.g. locating
<Header/>tag usages), which the Vue/Svelte servers add via Volar-specificrequests, are not implemented here and could be a follow-up.
.tsx/.jsxare opened with thetypescriptreact/javascriptreactlanguageids (as the base
TypeScriptLanguageServerdoes) so tsserver does not truncatesymbol ranges at JSX expressions; Astro projects commonly include these via
React/Preact/Solid integrations.
_start_servertears down the companion and the partial Astro process beforere-raising, to avoid leaking Node processes (mirrors the Angular server).
assert self._ts_server is not Noneinvariants follow thecurrent Vue/Svelte servers.