7.7 KiB
@volar/kit, @volar/editor, and @volar/monaco Deep Dive
Docs Index • Repo README • Plugin Authoring • Configuration & Projects • Alternative Editors
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
tsconfigprojects (optionally with project references). - Watching the file system for changes and triggering reloads/re-diagnostics.
- Exposing configuration hooks and restart commands.
Quickstart
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
- Single source for TypeScript – pass the same
tsobject everywhere (@volar/kit, plugin code, CLI) to avoid version mismatches. - Debounce reloads – if you implement custom watchers, wrap
project.reload()calls with a debounce to prevent thrashing. - 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
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
- Worker Registration
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
workeroutput.
- Virtual File System
Implement a file host used by the language service:
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
},
};
- Synchronizing Monaco Models
Listen to Monaco model events and forward them to Volar:
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/FileReaderin 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
- Shared Language Service Factory – keep your
createLanguageServicelogic in one module and import it in both LSP (+@volar/kit) and Monaco contexts to avoid divergence. - File System Abstraction – design a file host interface that works for Node (fs/promises) and browser (in-memory map or IndexedDB).
- Configuration Sync – if you expose custom settings, ensure both LSP + Monaco flows consume the same configuration object (
applyConfiguration(config)). - Version Alignment – lock
@volar/*package versions across server and client to prevent subtle mismatches. - 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.