mirror of
https://github.com/LukeHagar/volar-docs.git
synced 2025-12-06 04:22:01 +00:00
Saving inital Docs POC
This commit is contained in:
198
docs/volar-kit-and-editor.md
Normal file
198
docs/volar-kit-and-editor.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# `@volar/kit`, `@volar/editor`, and `@volar/monaco` Deep Dive
|
||||
|
||||
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Configuration & Projects](configuration-and-projects.md) • [Alternative Editors](editor-integration-alt.md)
|
||||
|
||||
These companion packages encapsulate Volar’s runtime wiring so you can spin up language servers, custom editors, or browser-based playgrounds without rebuilding the infrastructure each time. This guide documents every API surface, lifecycle hook, and best practice for:
|
||||
|
||||
- `@volar/kit` – batteries-included Node bootstrapper for LSP servers (watchers, configuration, project factories).
|
||||
- `@volar/editor` – minimal abstractions for embedding Volar in non-LSP editors (custom document/connection implementations).
|
||||
- `@volar/monaco` – drop-in helpers for wiring Volar’s language service into Monaco-based experiences (VS Code Web, StackBlitz, custom playgrounds).
|
||||
|
||||
## 1. `@volar/kit`
|
||||
|
||||
### Purpose
|
||||
|
||||
`@volar/kit` handles the repetitive work of:
|
||||
|
||||
- Scaffolding an LSP server with sensible defaults.
|
||||
- Loading `tsconfig` projects (optionally with project references).
|
||||
- Watching the file system for changes and triggering reloads/re-diagnostics.
|
||||
- Exposing configuration hooks and restart commands.
|
||||
|
||||
### Quickstart
|
||||
|
||||
```ts
|
||||
import { createLanguageServer } from '@volar/kit';
|
||||
import * as ts from 'typescript';
|
||||
import { createVueProject } from '@vue/language-core';
|
||||
|
||||
const server = createLanguageServer({
|
||||
ts,
|
||||
loadProjects(rootFolders) {
|
||||
return rootFolders.map((folder) =>
|
||||
createVueProject(ts, {
|
||||
rootUri: folder,
|
||||
tsconfig: `${folder.fsPath}/tsconfig.json`,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
server.start();
|
||||
```
|
||||
|
||||
### Lifecycle Hooks
|
||||
|
||||
| Hook | Signature | Use |
|
||||
| --- | --- | --- |
|
||||
| `onCreate` | `(server) => void` | Add custom commands, middleware, telemetry. |
|
||||
| `loadProjects` | `(folders: URI[]) => Project[]` | Return an array of project instances; each project exposes `getLanguageService` and TS integration hooks. |
|
||||
| `watchFilePatterns` | `string[]` | Additional globs for the internal FS watcher. |
|
||||
| `resolveConfig` | `(connection) => Promise<Config>` | Async configuration loader; automatically re-applied on `workspace/didChangeConfiguration`. |
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `ts` | TypeScript module (required). |
|
||||
| `tsLocalized` | Map of localized diagnostic messages (optional). |
|
||||
| `projectFeatures.workspaceDiagnostics` | Enable workspace diagnostics handling (`true` by default when the client supports it). |
|
||||
| `projectFeatures.interFileDependencies` | When `true`, diagnostics consider cross-file dependencies; `false` means per-file only. |
|
||||
| `projectFeatures.strict` | Equivalent to `vue-tsc --strict`; surfaces additional diagnostics. |
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Single source for TypeScript** – pass the same `ts` object everywhere (`@volar/kit`, plugin code, CLI) to avoid version mismatches.
|
||||
2. **Debounce reloads** – if you implement custom watchers, wrap `project.reload()` calls with a debounce to prevent thrashing.
|
||||
3. **Expose restart commands** – `server.commands.register('volar.restart', () => server.restart())` so users can force reload from the editor.
|
||||
|
||||
## 2. `@volar/editor`
|
||||
|
||||
### Purpose
|
||||
|
||||
Designed for editors that don’t use LSP (custom IDEs, in-house editors). It provides building blocks for:
|
||||
|
||||
- Managing documents (`TextDocument`, `SnapshotDocument`).
|
||||
- Routing requests to the language service without JSON-RPC.
|
||||
- Handling workspace edits, diagnostics, and commands manually.
|
||||
|
||||
### Key APIs
|
||||
|
||||
- `createEditorConnection(languageService)` – returns an object with methods mirroring LSP requests (`doValidation`, `doComplete`, etc.) but callable directly.
|
||||
- `SnapshotDocument` – text document implementation that tracks incremental updates and script snapshots.
|
||||
- `TextDocuments` – event emitter (`onDidOpen`, `onDidChangeContent`, etc.) you can wire to your editor’s change notifications.
|
||||
|
||||
### Usage Pattern
|
||||
|
||||
```ts
|
||||
import { createEditorConnection } from '@volar/editor';
|
||||
import { createLanguageService } from '@volar/language-service';
|
||||
|
||||
const language = createLanguage(...);
|
||||
const service = createLanguageService(language, plugins, env, project);
|
||||
const editor = createEditorConnection(service);
|
||||
|
||||
editor.documents.onDidOpen(({ document }) => {
|
||||
const diagnostics = editor.languageFeatures.getDiagnostics(document.uri);
|
||||
applyDiagnostics(document.uri, diagnostics);
|
||||
});
|
||||
|
||||
editor.documents.onDidChangeContent(({ document }) => {
|
||||
const completions = editor.languageFeatures.getCompletionItems(document.uri, position);
|
||||
showCompletions(completions);
|
||||
});
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
- Custom Electron apps that embed Monaco but want offline/deterministic routing.
|
||||
- In-house IDEs (e.g., game studios) that prefer direct API calls over LSP.
|
||||
|
||||
## 3. `@volar/monaco`
|
||||
|
||||
### Purpose
|
||||
|
||||
Wraps Volar’s language service to integrate seamlessly with Monaco editor (used in VS Code Web, StackBlitz, CodeSandbox, Vue SFC Playground).
|
||||
|
||||
### Setup Steps
|
||||
|
||||
1. **Worker Registration**
|
||||
|
||||
```ts
|
||||
import * as monaco from 'monaco-editor';
|
||||
import { setupLanguageServiceForMonaco } from '@volar/monaco';
|
||||
|
||||
setupLanguageServiceForMonaco(monaco, {
|
||||
ts: () => import('typescript'),
|
||||
service: () => import('your-volar-service-entry'),
|
||||
});
|
||||
```
|
||||
|
||||
- Provide async loaders for TypeScript and your language service bundle.
|
||||
- Bundlers like Vite or Webpack should treat the worker entry as `worker` output.
|
||||
|
||||
2. **Virtual File System**
|
||||
|
||||
Implement a file host used by the language service:
|
||||
|
||||
```ts
|
||||
const files = new Map<string, string>();
|
||||
|
||||
const fsHost = {
|
||||
readFile(uri: string) {
|
||||
return files.get(uri);
|
||||
},
|
||||
writeFile(uri: string, content: string) {
|
||||
files.set(uri, content);
|
||||
},
|
||||
getDirectoryEntries(uri: string) {
|
||||
// return child URIs
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
3. **Synchronizing Monaco Models**
|
||||
|
||||
Listen to Monaco model events and forward them to Volar:
|
||||
|
||||
```ts
|
||||
monaco.editor.onDidCreateModel((model) => {
|
||||
const uri = model.uri.toString();
|
||||
fsHost.writeFile(uri, model.getValue());
|
||||
model.onDidChangeContent(() => {
|
||||
fsHost.writeFile(uri, model.getValue());
|
||||
languageService.syncFile(uri, model.getValue());
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Bundling Tips
|
||||
|
||||
- Use dynamic imports to lazy-load the worker bundle.
|
||||
- Ensure TypeScript is available in the worker scope (via `ts = await loadTS()`).
|
||||
- Provide polyfills for `fetch`/`FileReader` in browsers that lack them.
|
||||
|
||||
### Features Enabled
|
||||
|
||||
- IntelliSense for `.vue`/custom files.
|
||||
- Diagnostics inline in Monaco (markers API).
|
||||
- Formatting via Monaco’s `languages.registerDocumentFormattingEditProvider`.
|
||||
|
||||
## Combining the Pieces
|
||||
|
||||
| Scenario | Recommended Package |
|
||||
| --- | --- |
|
||||
| Node-based LSP server | `@volar/kit` |
|
||||
| Custom desktop editor | `@volar/editor` |
|
||||
| Browser playground | `@volar/monaco` |
|
||||
| Hybrid (Node server + Monaco client) | `@volar/kit` on the backend + `@volar/monaco` on the frontend |
|
||||
|
||||
## Cross-Cutting Best Practices
|
||||
|
||||
1. **Shared Language Service Factory** – keep your `createLanguageService` logic in one module and import it in both LSP (+@volar/kit) and Monaco contexts to avoid divergence.
|
||||
2. **File System Abstraction** – design a file host interface that works for Node (fs/promises) and browser (in-memory map or IndexedDB).
|
||||
3. **Configuration Sync** – if you expose custom settings, ensure both LSP + Monaco flows consume the same configuration object (`applyConfiguration(config)`).
|
||||
4. **Version Alignment** – lock `@volar/*` package versions across server and client to prevent subtle mismatches.
|
||||
5. **Telemetry Parity** – forward the same telemetry/logging events from both LSP and Monaco contexts so you can compare editor behaviors.
|
||||
|
||||
By understanding the responsibilities of `@volar/kit`, `@volar/editor`, and `@volar/monaco`, you can implement Volar-powered experiences everywhere—from desktop editors to in-browser playgrounds—without reimplementing the plumbing each time.
|
||||
Reference in New Issue
Block a user