mirror of
https://github.com/LukeHagar/volar-docs.git
synced 2025-12-06 04:22:01 +00:00
95 lines
2.9 KiB
Markdown
95 lines
2.9 KiB
Markdown
# Building a Volar-Powered Monaco Playground
|
||
|
||
> [Docs Index](README.md) • [Repo README](../README.md) • [Volar Kit & Editor](volar-kit-and-editor.md) • [Live Examples](live-examples.md)
|
||
|
||
This guide walks through wiring Volar’s language service into a browser-based playground (Vue SFC REPL, documentation site, or sandbox) using `@volar/monaco`.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
Monaco Editor (main thread)
|
||
│
|
||
├─ Worker (language service)
|
||
│ ├─ TypeScript runtime
|
||
│ ├─ Volar language service
|
||
│ └─ Virtual file system (in-memory / IndexedDB)
|
||
└─ UI (tabs, preview)
|
||
```
|
||
|
||
## Step 1: Install Dependencies
|
||
|
||
```bash
|
||
npm install monaco-editor @volar/monaco typescript
|
||
```
|
||
|
||
## Step 2: Worker Setup
|
||
|
||
```ts
|
||
// volar.worker.ts
|
||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||
import { setupLanguageServiceForMonaco } from '@volar/monaco';
|
||
|
||
setupLanguageServiceForMonaco(monaco, {
|
||
loadTypescript: () => import('typescript/lib/typescript.js'),
|
||
loadLanguageService: () => import('./volar-service-entry'),
|
||
});
|
||
```
|
||
|
||
`volar-service-entry` exports `createLanguageService(env)` that mirrors your server’s factory.
|
||
|
||
## Step 3: Main Thread Wiring
|
||
|
||
```ts
|
||
import * as monaco from 'monaco-editor';
|
||
|
||
monaco.languages.register({ id: 'vue' });
|
||
monaco.languages.onLanguage('vue', () => {
|
||
new Worker(new URL('./volar.worker.ts', import.meta.url), { type: 'module' });
|
||
});
|
||
|
||
const model = monaco.editor.createModel('<template>hi</template>', 'vue', monaco.Uri.parse('file:///App.vue'));
|
||
monaco.editor.create(document.getElementById('editor')!, { model });
|
||
```
|
||
|
||
## Step 4: Virtual File System
|
||
|
||
Track files and sync them with the language service host:
|
||
|
||
```ts
|
||
const files = new Map<string, string>();
|
||
|
||
function syncModel(model: monaco.editor.ITextModel) {
|
||
const uri = model.uri.toString();
|
||
files.set(uri, model.getValue());
|
||
languageServiceHost.writeFile(uri, model.getValue());
|
||
}
|
||
|
||
monaco.editor.onDidCreateModel((model) => {
|
||
syncModel(model);
|
||
model.onDidChangeContent(() => syncModel(model));
|
||
});
|
||
```
|
||
|
||
Persist files using `localStorage`, IndexedDB, or encoded URLs so sessions can be shared.
|
||
|
||
## Step 5: Preview / Output Integration
|
||
|
||
- Render an iframe or use the Vue runtime to preview components.
|
||
- Listen for diagnostics via Monaco markers and show a summary list.
|
||
- Capture console output from the preview iframe for debugging.
|
||
|
||
## Performance Tips
|
||
|
||
1. Lazy-load worker bundle when a Vue file is opened.
|
||
2. Debounce file sync to avoid flooding the worker.
|
||
3. Reuse the same TypeScript module across workers (via CDN) to reduce bundle size.
|
||
4. For large projects, load files on demand instead of preloading entire repos.
|
||
|
||
## Example Projects
|
||
|
||
- [Vue SFC Playground](https://github.com/vuejs/repl)
|
||
- [StackBlitz Vue starter](https://stackblitz.com/edit/vue)
|
||
- [CodeSandbox Projects](https://codesandbox.io/p/dashboard)
|
||
|
||
By reusing your existing Volar language-service factory in the browser, you guarantee parity between editor IntelliSense and playground experiences.
|