Saving inital Docs POC

This commit is contained in:
Luke Hagar
2025-11-09 22:22:52 -06:00
commit 95f23e2eb3
35 changed files with 5457 additions and 0 deletions

View 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 Volars 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 Volars 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 dont 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 editors 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 Volars 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 Monacos `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.