7.0 KiB
Performance & Debugging Playbook
Volar’s language server is capable of analyzing massive Vue codebases, but only if you configure, profile, and debug it thoughtfully. This playbook covers every tool, environment flag, and technique for keeping your integrations fast and diagnosable.
Philosophies
- Measure first – Always gather profiles/metrics before optimizing.
- Avoid duplication – Hook into Volar’s existing document, project, and TypeScript caches instead of re-reading files.
- Fail loudly – Surface configuration errors, missing schemas, and long-running operations via logs/telemetry so users know what to fix.
Profiling Toolkit
1. Volar’s Built-in Tracing
Set environment variables before launching your LSP:
VOLAR_TRACE=protocol VOLAR_TRACE_DIR=/tmp/volar-trace npm run dev
protocol: logs LSP traffic (requests, responses).log: internal logging.
In VS Code, set "volar.vueserver.trace.server": "verbose" to mirror the same behavior.
2. CPU & Heap Snapshots
Run the server with --inspect:
node --inspect-brk dist/server.js
- Use Chrome DevTools or VS Code’s debugger to capture CPU profiles and heap snapshots.
- Focus on
provideDiagnosticsandprovideCompletionItemsif latency spikes while typing.
3. TypeScript Tracing
Enable TS compiler traces to see how TypeScript handles your virtual files:
{
"vueCompilerOptions": {},
"tsserver": {
"logDirectory": "./.ts-log",
"trace": "messages"
}
}
Review .ts-log/tsserver.log for slow operations (syntaxDiag, semanticDiag, etc.).
Hotspots & Mitigations
| Hotspot | Symptom | Mitigation |
|---|---|---|
| Diagnostics re-run on every keystroke | CPU spikes | Debounce validations (VALIDATION_DELAY), skip unchanged documents, skip workspace diagnostics unless requested. |
| Re-parsing large snapshots | Memory churn | Cache AST/metadata on IScriptSnapshot instances and reuse in language-service plugins. |
| Schema fetch storms | Network latency | Cache with cacheSchemas, support offline file:// URIs, respect HTTP cache headers. |
| Duplicate TypeScript programs | Slow TS features | Use createTypeScriptProject once per config and reuse the returned project. |
| Logging too verbose | Performance drag | Gate debug logging behind env vars / user settings. |
Debugging Scenarios
Scenario 1: Missing Diagnostics
Checklist:
- Verify
provideDiagnosticsruns by logging entry/exit. - Ensure mappings have
verification: true. - Confirm the document’s language ID matches your plugin’s target.
- Check
connection.sendDiagnosticsfor errors (watch the LSP trace).
Scenario 2: Incorrect Hover Text
- Inspect the generated virtual code (
codeGen.getText()) to ensure the content is what you expect. - Use
@volar/source-mapto verify the mapping covers the range you hovered. - Check that
provideHoverreturnsMarkdownString/stringin the format the client expects (no stray triple backticks unless intended).
Scenario 3: Memory Leak
- Take heap snapshots before/after opening large files.
- Confirm you dispose snapshots (call
disposeonIScriptSnapshotif you allocate custom ones). - Remove references to old projects/documents when workspace folders change (watch
server.workspaceFolders.onDidChange).
Scenario 4: Take Over Mode Conflicts
- Verify only one TypeScript server is running (disable built-in TS in VS Code when using Volar).
- Check
tsserver.logto ensure the Volar TS plugin is loaded once per project. - Provide documentation/instructions for users to disable conflicting extensions.
Logging & Diagnostics
Structured Logging
const log = (message: string, data?: unknown) => {
connection.console.info(`[json-yaml] ${message} ${data ? JSON.stringify(data) : ''}`);
};
- Use
connection.consolefor logs visible in the client’s output channel. - Reserve
console.errorfor actionable failures (schema fetch errors, config parse failures).
Telemetry Hooks
connection.telemetry.logEvent({
type: 'json-yaml.schemaFetch',
uri,
duration,
success,
});
- Emit events for expensive operations (schema fetch, TypeScript project reload) to help downstream clients aggregate data.
- Keep personally identifiable information out of telemetry payloads.
Work Done Progress
const progress = await connection.window.createWorkDoneProgress();
progress.begin('Fetching schemas', 0, 'Starting');
progress.report(50, 'Halfway');
progress.done();
- Use for long-running operations (initial project load, workspace diagnostics).
Best Practices for Performance-Sensitive Hooks
Diagnostics
- Track pending validations per URI; cancel the previous timer when new edits arrive.
- Compare document versions before publishing to avoid stale updates.
- Use workspace diagnostics sparingly; provide a manual “Refresh Diagnostics” command if they’re expensive in your environment.
Completions
- Precompute completion data (e.g., known components, JSON schema values) and reuse across requests.
- Return resolved items via
resolveCompletionItemonly when the client asks; keep the initial list lightweight.
Hovers
- Avoid heavy computation in
provideHover. Pre-parse metadata (docstrings, schema descriptions) when loading the project. - Cache hover results for stable nodes (component props) and invalidate when the underlying file changes.
TypeScript Integration
- Use
createTypeScriptProjectonce per config and share the result across all language services to leverage TS’s incremental caching. - Avoid calling
project.reload()unless absolutely necessary; prefer targeted invalidation (e.g.,language.scripts.delete(uri)).
Troubleshooting Checklist
- Is the issue limited to specific files? Inspect their generated output and mappings.
- Does it happen after hot reload or configuration changes? Check
applyConfigurationto ensure you’re not re-registering plugins or leaking state. - Is TypeScript raising errors? Examine
tsserver.logand ensure your TS plugin is registering correctly. - Are you duplicating watchers? One file watcher per project is sufficient; extra watchers can saturate the event loop.
- Is schema resolution slow? Provide
resolveWorkspaceUrito avoid hitting network paths unnecessarily and cache responses aggressively.
When to Ask for Help
- Capture logs (
VOLAR_TRACE,tsserver.log, heap/CPU profiles) and file an issue with the relevant data. - Include your
tsconfig/jsconfig, workspace structure, and steps to reproduce. - Mention whether Take Over Mode is enabled and which editor you’re using.
By following this playbook, you can keep Volar integrations responsive while making it easy to diagnose issues when they arise. Profile often, log judiciously, and share configuration knobs with your users so they can tailor performance to their workflow.