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:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
62
README.md
Normal file
62
README.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# VolarJS Documentation Hub
|
||||||
|
|
||||||
|
VolarJS powers Vue tooling across editors, yet the official docs are scattered. This repository collects practical, engineer-focused notes about the Volar language server so teams can understand, extend, and debug it without spelunking through source code.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
- Explain Volar architecture: core packages, key entrypoints, and how they plug into editor hosts.
|
||||||
|
- Document the LSP surface that Volar implements (capabilities, custom requests, known quirks).
|
||||||
|
- Share recipes for common tasks such as adding diagnostics, language features, or IDE integrations.
|
||||||
|
- Track ecosystem tooling (volar-service modules, language plugins, CLI utilities) and their compatibility.
|
||||||
|
- Publish getting-started and integration guides (e.g., combining Volar with JSON/YAML language services).
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
1. **Landscape survey** – catalogue existing blog posts, RFCs, and source references.
|
||||||
|
2. **LSP deep dive** – map each request / notification to the Volar handlers and note deviations from the spec.
|
||||||
|
3. **Extensibility guide** – document how to write and register custom language features.
|
||||||
|
4. **Debugging playbook** – logging, tracing, and profiling techniques for the Volar server process.
|
||||||
|
|
||||||
|
## Repo Structure
|
||||||
|
Planned directories:
|
||||||
|
- `docs/` – longer-form guides (architecture, extensibility, debugging). See the [docs index](docs/README.md) for a full table of contents.
|
||||||
|
- `docs/live-examples.md` – curated list of real projects using VolarJS (VS Code extension, Neovim/LSP integrations, online IDEs, CLI tooling, community plugins).
|
||||||
|
- Key guides (see [docs index](docs/README.md) for the full list):
|
||||||
|
- [`docs/getting-started.md`](docs/getting-started.md)
|
||||||
|
- [`docs/building-lsp-json-yaml.md`](docs/building-lsp-json-yaml.md)
|
||||||
|
- [`docs/plugin-authoring.md`](docs/plugin-authoring.md)
|
||||||
|
- [`docs/source-map-and-code-gen.md`](docs/source-map-and-code-gen.md)
|
||||||
|
- [`docs/configuration-and-projects.md`](docs/configuration-and-projects.md)
|
||||||
|
- [`docs/performance-and-debugging.md`](docs/performance-and-debugging.md)
|
||||||
|
- [`docs/testing-and-ci.md`](docs/testing-and-ci.md)
|
||||||
|
- [`docs/telemetry-and-observability.md`](docs/telemetry-and-observability.md)
|
||||||
|
- [`docs/workspace-diagnostics.md`](docs/workspace-diagnostics.md)
|
||||||
|
- [`docs/error-handling-and-resilience.md`](docs/error-handling-and-resilience.md)
|
||||||
|
- [`docs/ux-best-practices.md`](docs/ux-best-practices.md)
|
||||||
|
- [`docs/lsp-benchmarking.md`](docs/lsp-benchmarking.md)
|
||||||
|
- [`docs/troubleshooting-faq.md`](docs/troubleshooting-faq.md)
|
||||||
|
- [`docs/release-and-upgrade.md`](docs/release-and-upgrade.md)
|
||||||
|
- [`docs/vetur-migration.md`](docs/vetur-migration.md)
|
||||||
|
- [`docs/custom-diagnostics.md`](docs/custom-diagnostics.md)
|
||||||
|
- [`docs/monaco-playground.md`](docs/monaco-playground.md)
|
||||||
|
- [`docs/volar-kit-and-editor.md`](docs/volar-kit-and-editor.md)
|
||||||
|
- [`docs/typescript-plugin-deep-dive.md`](docs/typescript-plugin-deep-dive.md)
|
||||||
|
- [`docs/volar-service-catalog.md`](docs/volar-service-catalog.md)
|
||||||
|
- [`docs/cli-integration.md`](docs/cli-integration.md)
|
||||||
|
- [`docs/editor-integration-alt.md`](docs/editor-integration-alt.md)
|
||||||
|
- [`docs/cross-language-recipes.md`](docs/cross-language-recipes.md)
|
||||||
|
- [`docs/package-api-reference.md`](docs/package-api-reference.md)
|
||||||
|
- [`docs/response-formatting.md`](docs/response-formatting.md)
|
||||||
|
- [`docs/cross-language-recipes.md`](docs/cross-language-recipes.md)
|
||||||
|
- [`docs/package-api-reference.md`](docs/package-api-reference.md)
|
||||||
|
- [`docs/telemetry-and-observability.md`](docs/telemetry-and-observability.md)
|
||||||
|
- `reference/` – API and protocol notes, ideally generated from code comments over time.
|
||||||
|
- `examples/` – minimal projects showing custom Volar features in action.
|
||||||
|
- `examples/json-yaml-lsp` – runnable server that mixes Volar with JSON/YAML services.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
Issues and PRs are welcome. Please prefer:
|
||||||
|
- Real-world examples that demonstrate a concept.
|
||||||
|
- Links back to Volar source or specs for verification.
|
||||||
|
- Clear reproduction steps for bugs or editor quirks.
|
||||||
|
|
||||||
|
---
|
||||||
|
This repo is intentionally lightweight—start by dropping notes in Markdown, then iterate toward polished docs once content stabilizes.
|
||||||
51
docs/README.md
Normal file
51
docs/README.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# VolarJS Docs Index & Table of Contents
|
||||||
|
|
||||||
|
Use this index to jump to any guide. Each entry lists key sections so you can dive straight into the content you need.
|
||||||
|
|
||||||
|
## Core Guides
|
||||||
|
|
||||||
|
1. **[Getting Started](getting-started.md)** – mental model, package overview, installation, bootstrapping, integration paths, troubleshooting.
|
||||||
|
2. **[Building a JSON/YAML LSP](building-lsp-json-yaml.md)** – architecture, setup steps, performance/cancellation, diagnostics/quick fixes, packaging.
|
||||||
|
3. **[Plugin Authoring](plugin-authoring.md)** – language plugins, language-service plugins, TypeScript bridge, registration, best practices.
|
||||||
|
4. **[Source Map & Code Gen](source-map-and-code-gen.md)** – `@volar/source-map` and `@volar/code-gen` APIs, examples, debugging tips.
|
||||||
|
5. **[Configuration & Projects](configuration-and-projects.md)** – Take Over Mode, monorepos, multi-root workspaces, settings synchronization.
|
||||||
|
6. **[Performance & Debugging](performance-and-debugging.md)** – profiling, throttling, hotspots, telemetry hooks, troubleshooting scenarios.
|
||||||
|
7. **[Testing & CI](testing-and-ci.md)** – unit/integration testing, VS Code/Neovim harnesses, snapshot strategies, CI workflows.
|
||||||
|
8. **[Telemetry & Observability](telemetry-and-observability.md)** – logging, telemetry events, work-done progress, health endpoints.
|
||||||
|
9. **[Workspace Diagnostics](workspace-diagnostics.md)** – protocol primer, result IDs, incremental updates, partial results.
|
||||||
|
10. **[Error Handling & Resilience](error-handling-and-resilience.md)** – schema fallbacks, configuration validation, restart strategies.
|
||||||
|
11. **[UX Best Practices](ux-best-practices.md)** – progress indicators, notifications, settings UX, editor-specific guidance.
|
||||||
|
12. **[LSP Benchmarking](lsp-benchmarking.md)** – metrics, tooling (`lsp-bench`, custom harnesses), reporting, CI automation.
|
||||||
|
13. **[Troubleshooting & FAQ](troubleshooting-faq.md)** – quick fixes for duplicate diagnostics, schema fetch failures, logging.
|
||||||
|
14. **[Release & Upgrade Guide](release-and-upgrade.md)** – version matrix, upgrade checklist, CI matrices.
|
||||||
|
15. **[Vetur Migration](vetur-migration.md)** – step-by-step instructions for moving from Vetur to Volar.
|
||||||
|
16. **[Custom Diagnostics](custom-diagnostics.md)** – recipe for building project-specific diagnostic plugins.
|
||||||
|
17. **[Monaco Playground](monaco-playground.md)** – integrating Volar with browser playgrounds via `@volar/monaco`.
|
||||||
|
18. **[Volar Kit, Editor & Monaco](volar-kit-and-editor.md)** – bootstrap servers with `@volar/kit`, embed language services in custom editors, and integrate with Monaco.
|
||||||
|
19. **[TypeScript Plugin Deep Dive](typescript-plugin-deep-dive.md)** – `@volar/vue-typescript`, compiler options, macros, diagnostics control.
|
||||||
|
20. **[Volar Service Catalog](volar-service-catalog.md)** – official/community `volar-service-*` plugins and configuration patterns.
|
||||||
|
21. **[CLI Integration](cli-integration.md)** – building `vue-tsc`-style headless tooling, workspace diagnostics in CI.
|
||||||
|
22. **[Alternative Editor Integration](editor-integration-alt.md)** – guidance for JetBrains, Sublime, Neovim, Emacs, custom editors, VS Code Web.
|
||||||
|
23. **[Cross-Language Recipes](cross-language-recipes.md)** – adapting Volar’s architecture to Astro, Svelte, Marko, Tailwind-heavy setups, etc.
|
||||||
|
24. **[Package API Reference](package-api-reference.md)** – quick lookup for `@volar/editor`, `@volar/language-service`, `@volar/typescript`, etc.
|
||||||
|
25. **[Response Formatting & Content](response-formatting.md)** – best practices for diagnostics, hovers, completions, and code actions.
|
||||||
|
|
||||||
|
## Example Catalogue
|
||||||
|
|
||||||
|
- **[Live Examples](live-examples.md)** – curated list of editor extensions, online IDEs, CLI tooling, and community plugins powered by VolarJS.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- **[`examples/json-yaml-lsp`](../examples/json-yaml-lsp/README.md)** – runnable project combining Volar with JSON/YAML services, showcasing diagnostics, quick fixes, `$ref` definitions, workspace diagnostics, and configuration reloads.
|
||||||
|
|
||||||
|
## Suggested Reading Order
|
||||||
|
|
||||||
|
1. Start with **Getting Started** to grasp the core packages.
|
||||||
|
2. Follow with **Plugin Authoring** and **Source Map & Code Gen** if you plan to extend Volar.
|
||||||
|
3. Consult **Configuration & Projects** before rolling out to monorepos or multi-root workspaces.
|
||||||
|
4. Use **Performance**, **Testing**, **Telemetry**, and **Error Handling** guides to harden your server.
|
||||||
|
5. Refer to **UX Best Practices** and **Benchmarking** when polishing the user experience and ensuring regressions don’t slip in.
|
||||||
|
|
||||||
|
## Cross-Links
|
||||||
|
|
||||||
|
Every guide links back to this index via its navigation header. If you create new docs, add them here so the ecosystem stays discoverable.
|
||||||
457
docs/building-lsp-json-yaml.md
Normal file
457
docs/building-lsp-json-yaml.md
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
# Building a JSON/YAML-Aware LSP with VolarJS
|
||||||
|
|
||||||
|
[TOC](#table-of-contents) • [Getting Started](getting-started.md) • [Plugin Authoring](plugin-authoring.md) • [Performance](performance-and-debugging.md)
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Architecture Snapshot](#architecture-snapshot)
|
||||||
|
2. [Required Packages](#required-packages)
|
||||||
|
3. [Step 1 – Bootstrap a Volar Server Shell](#step-1--bootstrap-a-volar-server-shell)
|
||||||
|
4. [Step 2 – Wire JSON and YAML Services](#step-2--wire-json-and-yaml-services)
|
||||||
|
5. [Step 3 – Dispatch Requests Inside the LSP Server](#step-3--dispatch-requests-inside-the-lsp-server)
|
||||||
|
6. [Step 4 – Capabilities and Initialization](#step-4--capabilities-and-initialization)
|
||||||
|
7. [Step 5 – LSP-Friendly Performance & Cancellation](#step-5--lsp-friendly-performance--cancellation)
|
||||||
|
8. [Diagnostics, Quick Fixes, and `$ref` Definitions](#diagnostics-quick-fixes-and-ref-definitions)
|
||||||
|
9. [Testing the Server](#testing-the-server)
|
||||||
|
10. [Operational Considerations](#operational-considerations)
|
||||||
|
11. [Packaging & Distribution](#packaging--distribution)
|
||||||
|
12. [Next Steps](#next-steps)
|
||||||
|
|
||||||
|
This guide walks through composing a custom language server that combines Volar’s connection/runtime utilities with the existing VS Code JSON and Red Hat YAML services. The goal: a single LSP binary that understands structured config files while leaving room for your own Volar plugins.
|
||||||
|
|
||||||
|
## Architecture Snapshot
|
||||||
|
|
||||||
|
```
|
||||||
|
Client (VS Code / Neovim / Monaco LSP) ─▶ @volar/language-server
|
||||||
|
├─▶ Volar language service (custom plugins)
|
||||||
|
├─▶ vscode-json-languageservice
|
||||||
|
└─▶ yaml-language-server (service API)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `@volar/language-server` supplies the transport, file watching, and initialization plumbing.
|
||||||
|
- The Volar service handles any custom documents you plug in later (left empty in this starter).
|
||||||
|
- JSON and YAML requests are delegated to their respective language service implementations. You route incoming LSP requests to the right backend based on document URI / languageId.
|
||||||
|
|
||||||
|
## Required Packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev \
|
||||||
|
@volar/language-server \
|
||||||
|
vscode-json-languageservice \
|
||||||
|
yaml-language-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional helpers:
|
||||||
|
|
||||||
|
- `@volar/kit` if you prefer a batteries-included bootstrapper for config + watch.
|
||||||
|
- `volar-service-<feature>` plugins for any custom diagnostics or editor behaviors you plan to add later.
|
||||||
|
|
||||||
|
## Step 1 – Bootstrap a Volar Server Shell
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server/host.ts
|
||||||
|
import { createConnection, createServer } from '@volar/language-server/node';
|
||||||
|
import { createSimpleProject } from '@volar/language-server/lib/project/simpleProject';
|
||||||
|
|
||||||
|
export const connection = createConnection();
|
||||||
|
export const server = createServer(connection);
|
||||||
|
|
||||||
|
const project = createSimpleProject([]);
|
||||||
|
const languageServicePlugins = [];
|
||||||
|
|
||||||
|
connection.onInitialize((params) => server.initialize(params, project, languageServicePlugins));
|
||||||
|
connection.onInitialized(() => server.initialized());
|
||||||
|
connection.onShutdown(() => server.shutdown());
|
||||||
|
```
|
||||||
|
|
||||||
|
- `createSimpleProject([])` wires Volar’s document tracking and transport without custom language plugins. Swap in your own `LanguagePlugin` array whenever you add domain-specific logic.
|
||||||
|
- `languageServicePlugins` is where you register Volar service plugins (`volar-service-*`) should you need additional completions, hovers, or refactors for your target language.
|
||||||
|
|
||||||
|
## Step 2 – Wire JSON and YAML Services
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server/jsonYaml.ts
|
||||||
|
import { getLanguageService as createJsonService } from 'vscode-json-languageservice';
|
||||||
|
import { getLanguageService as createYamlService } from 'yaml-language-server';
|
||||||
|
|
||||||
|
export function createJsonYamlServices() {
|
||||||
|
const json = createJsonService({
|
||||||
|
schemaRequestService: async (uri) => fetchSchema(uri),
|
||||||
|
workspaceContext: {
|
||||||
|
resolveRelativePath(relativePath, resource) {
|
||||||
|
try {
|
||||||
|
const base = new URL(resource);
|
||||||
|
return new URL(relativePath, base).toString();
|
||||||
|
} catch {
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contributions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const yaml = createYamlService({
|
||||||
|
schemaRequestService: async (uri) => fetchSchema(uri),
|
||||||
|
workspaceContext: {
|
||||||
|
resolveRelativePath(relativePath, resource) {
|
||||||
|
try {
|
||||||
|
const base = new URL(resource);
|
||||||
|
return new URL(relativePath, base).toString();
|
||||||
|
} catch {
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
telemetry: { send: () => {}, sendError: () => {}, sendTrack: () => {} },
|
||||||
|
clientCapabilities: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { json, yaml };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- Implement `fetchSchema` to load schemas from HTTP, file system, or an internal registry.
|
||||||
|
- Cache schemas aggressively; both services expect memoized responses for performance.
|
||||||
|
- Use `server.workspaceFolders` (see the example) to resolve relative schema paths so `foo/schema.json` can be fetched without hard-coding a root.
|
||||||
|
|
||||||
|
## Step 3 – Dispatch Requests Inside the LSP Server
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server/main.ts
|
||||||
|
import { URI, Utils } from 'vscode-uri';
|
||||||
|
import { connection, server } from './host';
|
||||||
|
import { createJsonYamlServices } from './jsonYaml';
|
||||||
|
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
|
||||||
|
const structured = createJsonYamlServices({
|
||||||
|
resolveWorkspaceUri(relativePath) {
|
||||||
|
const [root] = server.workspaceFolders.all;
|
||||||
|
return root ? Utils.joinPath(root, relativePath).toString() : undefined;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const pendingValidation = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
const VALIDATION_DELAY = 200;
|
||||||
|
|
||||||
|
connection.onInitialized(async () => {
|
||||||
|
server.initialized();
|
||||||
|
await applyConfiguration();
|
||||||
|
server.configurations.onDidChange(applyConfiguration);
|
||||||
|
server.documents.all().forEach((document) => queueValidation(document.uri));
|
||||||
|
});
|
||||||
|
|
||||||
|
server.documents.onDidOpen(({ document }) => queueValidation(document.uri));
|
||||||
|
server.documents.onDidChangeContent(({ document }) => queueValidation(document.uri));
|
||||||
|
server.documents.onDidClose(({ document }) => {
|
||||||
|
cancelQueuedValidation(document.uri);
|
||||||
|
connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workspace diagnostics: editors can fetch full workspace snapshots when supported
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (_params, token) => {
|
||||||
|
const items = [];
|
||||||
|
for (const doc of server.documents.all()) {
|
||||||
|
if (token.isCancellationRequested) break;
|
||||||
|
items.push({
|
||||||
|
uri: doc.uri,
|
||||||
|
kind: DocumentDiagnosticReportKind.Full,
|
||||||
|
version: doc.version ?? null,
|
||||||
|
items: await collectDiagnostics(doc),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { items };
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onCompletion(async (params, token) => {
|
||||||
|
if (token.isCancellationRequested) return null;
|
||||||
|
const doc = getDocument(params.textDocument.uri);
|
||||||
|
if (!doc) return null;
|
||||||
|
|
||||||
|
const backend = pickBackend(doc);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structured.completeJson(doc, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structured.completeYaml(doc, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onHover(async (params, token) => {
|
||||||
|
if (token.isCancellationRequested) return null;
|
||||||
|
const doc = getDocument(params.textDocument.uri);
|
||||||
|
if (!doc) return null;
|
||||||
|
|
||||||
|
const backend = pickBackend(doc);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structured.hoverJson(doc, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structured.hoverYaml(doc, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function validate(document: TextDocument) {
|
||||||
|
const diagnostics = await collectDiagnostics(document);
|
||||||
|
|
||||||
|
const latest = getDocument(document.uri);
|
||||||
|
if (!latest || latest.version !== document.version) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.sendDiagnostics({ uri: document.uri, diagnostics });
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueValidation(uri: string) {
|
||||||
|
cancelQueuedValidation(uri);
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
pendingValidation.delete(uri);
|
||||||
|
const doc = getDocument(uri);
|
||||||
|
if (doc) {
|
||||||
|
validate(doc);
|
||||||
|
}
|
||||||
|
}, VALIDATION_DELAY);
|
||||||
|
pendingValidation.set(uri, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelQueuedValidation(uri: string) {
|
||||||
|
const handle = pendingValidation.get(uri);
|
||||||
|
if (handle) {
|
||||||
|
clearTimeout(handle);
|
||||||
|
pendingValidation.delete(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectDiagnostics(document: TextDocument) {
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
return structured.validateJson(document);
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
return structured.validateYaml(document);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocument(uri: string): TextDocument | undefined {
|
||||||
|
return server.documents.get(URI.parse(uri)) as TextDocument | undefined;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Tip: This snippet imports `DocumentDiagnosticReportKind` from `vscode-languageserver/node` to mark each workspace diagnostic report as `Full`.
|
||||||
|
|
||||||
|
Routing Strategy:
|
||||||
|
|
||||||
|
1. Check `textDocument.languageId` (if the client sets it) and fall back to file extension.
|
||||||
|
2. If neither JSON nor YAML match, forward the request to Volar.
|
||||||
|
3. For JSON/YAML services, you must manage document snapshots—use `connection.workspace.getTextDocument(uri)` or maintain a `TextDocuments` instance to keep their APIs synchronized.
|
||||||
|
|
||||||
|
## Step 4 – Capabilities and Initialization
|
||||||
|
|
||||||
|
Make sure the combined server advertises the correct capabilities:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.onInitialize(() => ({
|
||||||
|
capabilities: {
|
||||||
|
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||||
|
completionProvider: { triggerCharacters: ['.', '"', '/', '<', ':'] },
|
||||||
|
hoverProvider: true,
|
||||||
|
documentFormattingProvider: true,
|
||||||
|
schemaRequestService: true,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
- JSON/YAML features may have narrower trigger characters; merge the superset so clients activate correctly.
|
||||||
|
- Expose custom `initializationOptions` to toggle schemas, plugin sets, or Take Over Mode.
|
||||||
|
|
||||||
|
> **Example project:** `examples/json-yaml-lsp/` contains a runnable implementation that wires the same services using the bare `@volar/language-server` utilities so you can attach any editor client immediately.
|
||||||
|
|
||||||
|
## Step 5 – LSP-Friendly Performance & Cancellation
|
||||||
|
|
||||||
|
To stay responsive on large workspaces:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const pendingValidation = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
const VALIDATION_DELAY = 200;
|
||||||
|
|
||||||
|
function queueValidation(uri: string) {
|
||||||
|
cancelQueuedValidation(uri);
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
pendingValidation.delete(uri);
|
||||||
|
const document = getDocument(uri);
|
||||||
|
if (document) {
|
||||||
|
await validateDocument(document);
|
||||||
|
}
|
||||||
|
}, VALIDATION_DELAY);
|
||||||
|
pendingValidation.set(uri, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.onCompletion(async (params, token) => {
|
||||||
|
if (token.isCancellationRequested) return null;
|
||||||
|
const document = getDocument(params.textDocument.uri);
|
||||||
|
if (!document) return null;
|
||||||
|
|
||||||
|
const result = await structured.completeJson(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
});
|
||||||
|
|
||||||
|
server.documents.onDidClose(({ document }) => {
|
||||||
|
cancelQueuedValidation(document.uri);
|
||||||
|
connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
async function applyConfiguration() {
|
||||||
|
const config =
|
||||||
|
(await server.configurations.get<JsonYamlConfiguration>('volarJsonYaml', undefined)) ?? {};
|
||||||
|
structured.configure(config);
|
||||||
|
await server.languageFeatures.requestRefresh(false);
|
||||||
|
server.documents.all().forEach((document) => queueValidation(document.uri));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Debounce validations** so rapid edits don’t trigger repeated schema checks.
|
||||||
|
- **Gate diagnostics by version** (after awaiting async work, re-fetch the document and compare `version` before sending).
|
||||||
|
- **Honor cancellation tokens** on hover/completion so abandoned requests don’t waste CPU.
|
||||||
|
- **Clear diagnostics on close** to avoid stale warnings when a document leaves the workspace.
|
||||||
|
- **Reflect configuration changes** via `server.configurations.get/onDidChange` and call `server.languageFeatures.requestRefresh` so clients re-query capabilities when settings toggle.
|
||||||
|
- The sample exports a `JsonYamlConfiguration` helper type so the configuration payload is strongly typed on both server and JSON/YAML helper layers.
|
||||||
|
- **Workspace diagnostics**: register `connection.languages.diagnostics.onWorkspace` so editors that support the capability (VS Code Insiders, Neovim, etc.) can request batched diagnostics across every open file.
|
||||||
|
- Clients can provide that payload via normal LSP configuration plumbing (e.g., VS Code settings):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volarJsonYaml": {
|
||||||
|
"json": {
|
||||||
|
"schemas": [{ "fileMatch": ["foo.json"], "url": "./schemas/foo.schema.json" }]
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"customTags": ["!secret scalar"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The sample project implements the same helpers so you can copy the exact patterns.
|
||||||
|
|
||||||
|
## Diagnostics, Quick Fixes, and `$ref` Definitions
|
||||||
|
|
||||||
|
With the wiring above you can progressively light up richer language features:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.onCodeAction(async (params, token) => {
|
||||||
|
if (token.isCancellationRequested) return null;
|
||||||
|
const doc = getDocument(params.textDocument.uri);
|
||||||
|
if (!doc) return null;
|
||||||
|
|
||||||
|
if (pickBackend(doc) === 'json') {
|
||||||
|
// Offers “Add $schema reference” quick fix whenever the root object lacks $schema.
|
||||||
|
return await structured.codeActionsJson(doc, params);
|
||||||
|
}
|
||||||
|
if (pickBackend(doc) === 'yaml') {
|
||||||
|
return await structured.codeActionsYaml(doc, params);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onDefinition(async (params, token) => {
|
||||||
|
if (token.isCancellationRequested) return null;
|
||||||
|
const doc = getDocument(params.textDocument.uri);
|
||||||
|
if (!doc) return null;
|
||||||
|
return pickBackend(doc) === 'json'
|
||||||
|
? structured.definitionJson(doc, params.position) // navigates $ref targets, component refs, etc.
|
||||||
|
: structured.definitionYaml(doc, params.position);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Diagnostics events** still flow through `validateDocument`, but now the handlers above can surface follow-up quick fixes (e.g., add a `$schema`, insert missing YAML keys) tied to the same document.
|
||||||
|
- **Auto-fix suggestions** leverage the YAML service’s built-in `getCodeAction` plus the JSON helper’s custom fix builder so editors can surface actionable “Quick Fix…” entries.
|
||||||
|
- **Hover context** already goes through the JSON/YAML services, so schema descriptions and enum docs populate tooltips automatically.
|
||||||
|
- **Definition requests** reuse `findDefinition` / `doDefinition`, which means `$ref` values in JSON or anchors/aliases in YAML jump straight to their targets.
|
||||||
|
|
||||||
|
## Testing the Server
|
||||||
|
|
||||||
|
1. Use the `vscode-languageclient` sample extension to spin up the server and ensure `.json` and `.yaml` each get the expected diagnostics.
|
||||||
|
2. Add fixtures under `examples/fixtures/` (one per file type) and run automated LSP probes via `vscode-languageserver/node`’s `createConnection` in tests.
|
||||||
|
3. Validate schema fetching offline by stubbing the request service.
|
||||||
|
|
||||||
|
## Best Practices Checklist (Apply All of Them)
|
||||||
|
|
||||||
|
1. **Single source of truth for documents** – always read file content through Volar’s `server.documents` API so completions, hovers, diagnostics, and code actions see the same in-memory snapshot.
|
||||||
|
2. **Cache aggressively** – schema downloads, JSON document parses, and YAML ASTs should be re-used whenever possible. The sample’s `cacheSchemas` + `getJsonDocument` are patterns to copy.
|
||||||
|
3. **Respect cancellation tokens everywhere** – every `connection.on*` handler should short-circuit when `token.isCancellationRequested` is true, even after `await`.
|
||||||
|
4. **Debounce diagnostics** – never bombard the client with validation spam. Use the `queueValidation` helper in the sample as a template for other expensive tasks.
|
||||||
|
5. **Version-gate publishDiagnostics** – after running async work, re-fetch `server.documents.get()` and compare `version` before calling `sendDiagnostics`.
|
||||||
|
6. **Clear diagnostics on close** – send `[]` when a document is closed so stale warnings vanish from the Problems panel.
|
||||||
|
7. **Implement workspace diagnostics** – if the client supports `workspace/diagnostic`, return a `WorkspaceFullDocumentDiagnosticReport` for each document; otherwise rely on standard document-level `textDocument/publishDiagnostics`.
|
||||||
|
8. **Single queue for configuration** – keep a shared `applyConfiguration` function that reconfigures your JSON/YAML helpers, requests a capability refresh, and re-validates open documents. Guard it with throttling if your editor emits frequent configuration updates.
|
||||||
|
9. **Expose strongly typed settings** – the `JsonYamlConfiguration` interface keeps JSON/YAML configuration honest; use it end-to-end so TypeScript catches mismatched schema settings.
|
||||||
|
10. **Honor schema-relative resolution** – combine `server.workspaceFolders` with `Utils.joinPath` so workspace-relative schema references resolve consistently between CLI, VS Code, and other editors.
|
||||||
|
|
||||||
|
### Diagnostics Lifecycle
|
||||||
|
|
||||||
|
- **Collect once, reuse everywhere** – the `collectDiagnostics` helper powers both `publishDiagnostics` and the workspace handler, so the behavior is identical regardless of how the client asks for diagnostics.
|
||||||
|
- **Workspace diagnostics** – always return `WorkspaceDocumentDiagnosticReport` objects with `kind: DocumentDiagnosticReportKind.Full` (or `Unchanged` if you implement result IDs). Include `version` so incremental clients know which snapshot produced the report.
|
||||||
|
- **Auto-fixes** – pair every diagnostic family with at least one actionable code action. The sample shows how to generate a “Add $schema” quick fix for JSON while delegating YAML fixes to the upstream service.
|
||||||
|
|
||||||
|
### Hover/Completion Detail
|
||||||
|
|
||||||
|
- Let schema-driven services provide the content, but normalize the results if needed (e.g., strip fenced code blocks, deduplicate markdown). Volar encourages leaving formatting to the service so you get free upgrades as those services improve.
|
||||||
|
- Remember to include trigger characters that match your schema’s syntax (`"`, `<`, `/`, etc.) so completions fire exactly when the client expects them.
|
||||||
|
|
||||||
|
### Definitions & `$ref` Intellisense
|
||||||
|
|
||||||
|
- Forward `textDocument/definition` to both JSON and YAML services—those implementations already understand `$ref`, component references, and YAML anchors.
|
||||||
|
- Always return `DefinitionLink[]` rather than raw `Location[]`, so editors that support peek views can highlight the exact range of the definition.
|
||||||
|
|
||||||
|
### Code Actions + Quick Fixes
|
||||||
|
|
||||||
|
- Respect the client’s `context.only` filter when building actions. The sample JSON quick fix bails out unless `CodeActionKind.QuickFix` is requested.
|
||||||
|
- Provide descriptive titles that match the editor UX. “Add $schema reference” reads better than “Insert snippet”.
|
||||||
|
|
||||||
|
### Schema Strategy
|
||||||
|
|
||||||
|
- Treat schema URIs the same way the CLI would: handle `http(s)://`, `file://`, and workspace-relative paths. The `resolveWorkspaceUri` callback plus `normalizeUri` implementation show how to cover every case.
|
||||||
|
- Expose per-folder overrides with globbing (e.g., `config/*.json`) and keep defaults in `volarJsonYaml.json.defaultSchemaUri`.
|
||||||
|
|
||||||
|
### Testing & Observability
|
||||||
|
|
||||||
|
- Add an integration test harness that spins up the LSP server with `vscode-languageserver/node` and issues representative LSP requests (hover, completion, diagnostics, workspace/diagnostic). Compare the responses to snapshots.
|
||||||
|
- Log schema fetches and large diagnostics only when `process.env.VOLAR_DIAGNOSTICS_DEBUG` (or similar) is enabled to avoid spamming the client in normal operation.
|
||||||
|
- Implement `connection.telemetry.logEvent` for significant events (schema download failures, YAML validation errors) so consuming editors can surface them in their telemetry pipelines if desired.
|
||||||
|
|
||||||
|
### Performance Knobs to Keep Handy
|
||||||
|
|
||||||
|
- `VALIDATION_DELAY` – tune this constant per project; increase it for massive monorepos, decrease it for tiny repos.
|
||||||
|
- `pendingValidation` map – always cancel old timers when new edits arrive to avoid stacking multiple validations for the same document.
|
||||||
|
- `applyConfiguration` – wrap the internals in a try/catch that logs configuration errors and falls back to the previous known-good settings so a malformed config doesn’t brick the server mid-session.
|
||||||
|
|
||||||
|
## Operational Considerations
|
||||||
|
|
||||||
|
- **Performance**: JSON and YAML services are CPU-light compared to Vue. Keep them stateless and reuse schema caches to avoid blocking Volar’s TypeScript workers.
|
||||||
|
- **Telemetry**: The YAML service expects a telemetry sink; provide a no-op logger or hook into your analytics pipeline.
|
||||||
|
- **Take Over Mode**: If your editor uses Take Over Mode (Volar replacing the default TS server), ensure the JSON/YAML portions do not register duplicate document selectors.
|
||||||
|
- **Schema Distribution**: For enterprise deployments, serve schemas from a local file share or CDN and pass that base URL through `initializationOptions`.
|
||||||
|
|
||||||
|
## Packaging & Distribution
|
||||||
|
|
||||||
|
1. **Editor extension** – bundle the server script with a VS Code/Neovim/Sublime extension and launch it over stdio (`node dist/server.js`). The sample project’s `npm run build` output is ready for this flow.
|
||||||
|
2. **Single binary** – tools like `pkg`, `nexe`, or `esbuild --platform=node` can squash the server (and schemas) into an executable for locked-down environments. Keep schema fetching pluggable so air-gapped installs can point to `file://` URIs.
|
||||||
|
3. **Hosted worker** – for web editors (Monaco, CodeSandbox) run the server logic inside a worker using `@volar/monaco` plus the same JSON/YAML services, forwarding messages over `postMessage`.
|
||||||
|
4. **CLI utility** – expose a CLI command (e.g., `volar-json-yaml check path/to/workspace`) that spins up the same JSON/YAML helpers without the LSP transport for CI validation; reuse the shared connection setup from the sample.
|
||||||
|
|
||||||
|
Whichever path you choose, keep the example project as a reference implementation so contributors can run `npm install && npm run dev` to replicate issues or experiment with new plugins.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Package the server with `pkg`/`nexe` or ship it via an editor extension.
|
||||||
|
2. Layer additional `volar-service-*` plugins (e.g., Tailwind, Prettier) as needed.
|
||||||
|
3. Add integration tests that open mixed-workspace folders to confirm routing stays correct.
|
||||||
|
|
||||||
|
With this pattern you can keep leveraging Volar’s Vue expertise while borrowing the battle-tested JSON/YAML implementations that already exist—no need to reinvent highlight/completion logic for those file types.
|
||||||
153
docs/cli-integration.md
Normal file
153
docs/cli-integration.md
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# CLI Integration & Headless Workflows
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Workspace Diagnostics](workspace-diagnostics.md) • [Testing & CI](testing-and-ci.md) • [LSP Benchmarking](lsp-benchmarking.md)
|
||||||
|
|
||||||
|
Language intelligence isn’t limited to editors—teams often run Volar-powered checks in CI, pre-commit hooks, or bespoke tooling. This guide shows how to build CLI utilities (à la `vue-tsc`) that reuse your Volar language service for headless diagnostics, formatting, or codegen tasks.
|
||||||
|
|
||||||
|
## Approaches
|
||||||
|
|
||||||
|
| Approach | Pros | Cons |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Reuse `vue-tsc` | Fast to adopt, maintained by Vue team | Limited to diagnostics; less customizable |
|
||||||
|
| Custom CLI (Node) | Full control, integrate custom plugins/services | Must manage FS, config, output formatting |
|
||||||
|
| LSP client CLI | Reuse existing LSP server, speak JSON-RPC | More setup (start server, send requests) |
|
||||||
|
|
||||||
|
## Building a Custom CLI
|
||||||
|
|
||||||
|
### 1. Shared Language Service Factory
|
||||||
|
|
||||||
|
Factor your language service creation into a reusable module (used by LSP + CLI):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export function createVolarService(tsconfigPath: string, config: MyConfig) {
|
||||||
|
const tsProject = createTypeScriptProject(tsconfigPath);
|
||||||
|
const core = createLanguageCore({ plugins: [...], ts: tsProject.typescript });
|
||||||
|
return createLanguageService(core, [volarServiceCss(), customService(config)], env, project);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. CLI Entrypoint
|
||||||
|
|
||||||
|
```ts
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import { URI } from 'vscode-uri';
|
||||||
|
import glob from 'fast-glob';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { createVolarService } from './service';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const service = createVolarService(process.argv[2] ?? 'tsconfig.json', loadConfig());
|
||||||
|
const files = await glob(['src/**/*.vue', 'src/**/*.ts']);
|
||||||
|
|
||||||
|
let hasErrors = false;
|
||||||
|
for (const file of files) {
|
||||||
|
const uri = URI.file(file).toString();
|
||||||
|
const diagnostics = await service.getDiagnostics(uri);
|
||||||
|
diagnostics.forEach((diag) => {
|
||||||
|
hasErrors = hasErrors || diag.severity === 1;
|
||||||
|
console.log(formatDiagnostic(file, diag));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(hasErrors ? 1 : 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Formatting Output
|
||||||
|
|
||||||
|
Use a standard format (VS Code-style) so editors/CI parse easily:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function formatDiagnostic(file: string, diag: Diagnostic) {
|
||||||
|
const range = `${diag.range.start.line + 1}:${diag.range.start.character + 1}`;
|
||||||
|
const severity = ['Error', 'Warning', 'Information', 'Hint'][diag.severity ?? 0];
|
||||||
|
return `${chalk.gray(file)}:${range} - ${severity} ${diag.message} (${diag.source})`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. JSON Output (Optional)
|
||||||
|
|
||||||
|
Provide `--json` flag to emit machine-readable diagnostics:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify({ uri, diagnostics }, null, 2));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Leveraging `workspace/diagnostic`
|
||||||
|
|
||||||
|
Instead of looping per file, reuse the workspace diagnostics pipeline:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const report = await service.context.project.context.languageService.getWorkspaceDiagnostics();
|
||||||
|
report.forEach((docReport) => {
|
||||||
|
docReport.items.forEach((diag) => {
|
||||||
|
console.log(formatDiagnostic(URI.parse(docReport.uri).fsPath, diag));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures CLI output matches editor diagnostics exactly.
|
||||||
|
|
||||||
|
## Integrating with CI
|
||||||
|
|
||||||
|
1. Add a script (`volar-check`) that runs your CLI.
|
||||||
|
2. Wire it into CI workflows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- run: npm run volar-check -- --json > artifacts/volar.json
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: volar-diagnostics
|
||||||
|
path: artifacts/volar.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Fail the pipeline when exit code ≠ 0.
|
||||||
|
|
||||||
|
## Pre-commit Hooks
|
||||||
|
|
||||||
|
Use `lint-staged` or `husky` to run targeted diagnostics:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{vue,ts,js}": "volar-check --files"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Implement `--files` flag to accept a comma-separated list and restrict diagnostics.
|
||||||
|
|
||||||
|
## Formatting & Code Actions
|
||||||
|
|
||||||
|
You can expose formatting or quick fixes via CLI:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const edits = await service.getDocumentFormattingEdits(uri, { tabSize: 2, insertSpaces: true }, undefined);
|
||||||
|
applyEditsToFile(fsPath, edits);
|
||||||
|
```
|
||||||
|
|
||||||
|
For code actions, use `service.getCodeActions` and apply `WorkspaceEdit`.
|
||||||
|
|
||||||
|
## Logging & Telemetry
|
||||||
|
|
||||||
|
- Print progress (“Analyzing 123 files…”) with timestamps.
|
||||||
|
- Collect metrics (time per file, total diagnostics) to feed into benchmarking dashboards.
|
||||||
|
|
||||||
|
## Packaging & Distribution
|
||||||
|
|
||||||
|
- Bundle with `pkg`/`nexe` if users lack Node.
|
||||||
|
- Provide `npx` entry for quick runs (`npx volar-check`).
|
||||||
|
- Document environment variables (`VOLAR_TRACE`, `VOLAR_CONFIG_PATH`) to customize behavior.
|
||||||
|
|
||||||
|
## Best Practices Checklist
|
||||||
|
|
||||||
|
- [ ] Shared service factory reused by both LSP and CLI.
|
||||||
|
- [ ] Configurable input (`tsconfig`, glob patterns, file list).
|
||||||
|
- [ ] Structured output (text + optional JSON).
|
||||||
|
- [ ] Proper exit codes (0 = success, 1 = errors, 2 = config failure).
|
||||||
|
- [ ] Support for ignoring warnings (e.g., `--max-warnings`).
|
||||||
|
- [ ] Telemetry/logging toggles for verbose debugging.
|
||||||
|
|
||||||
|
By bringing Volar’s language service into CLI form, you align editor diagnostics with automated checks, prevent regressions before they hit production, and empower teams to build custom workflows (codemods, linting, documentation pipelines) on top of the same intelligence.
|
||||||
190
docs/configuration-and-projects.md
Normal file
190
docs/configuration-and-projects.md
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
# Advanced Configuration & Project Shapes
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Getting Started](getting-started.md) • [Performance Guide](performance-and-debugging.md)
|
||||||
|
|
||||||
|
Volar can power everything from tiny single-file experiments to massive monorepos with multiple editors attached. This guide documents every configuration option, project topology, and editor integration knob so you can tune Volar for any workspace.
|
||||||
|
|
||||||
|
## Configuration Surfaces
|
||||||
|
|
||||||
|
| Surface | Who controls it | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `vueCompilerOptions` in `tsconfig.json` / `jsconfig.json` | Project authors | Vue-specific compiler tweaks (reactivity transform, macros, experimental features). |
|
||||||
|
| Volar client settings (VS Code “Volar” section, Neovim config, etc.) | End users | Editor integration options (Take Over Mode, diagnostic toggles, traces). |
|
||||||
|
| `server.configurations` (`volarJsonYaml` in our example) | Language server authors | Custom feature flags for your own plugins/services. |
|
||||||
|
| CLI flags/environment variables | Tooling authors | Overrides for headless usage (e.g., `VOLAR_TRACE=protocol` for logging). |
|
||||||
|
|
||||||
|
### Volar Client Settings (VS Code)
|
||||||
|
|
||||||
|
Common keys in `settings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volar.takeOverMode.enabled": true,
|
||||||
|
"volar.tsPlugin": true,
|
||||||
|
"volar.vueserver.log": "verbose",
|
||||||
|
"volar.diagnostics.onChange": true,
|
||||||
|
"volar.autoCompleteRefs": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `takeOverMode.enabled`: Replace VS Code’s TypeScript LS with Volar’s integrated server for `.ts`/`.js` files (required for advanced template-inferred types). When true, ensure only one TS server runs to avoid duplicate diagnostics.
|
||||||
|
- `tsPlugin`: Enables Volar’s TypeScript plugin so `tsserver` understands `.vue`.
|
||||||
|
- `diagnostics.onChange`: Live diagnostics while typing vs on save.
|
||||||
|
- `vueserver.log`: `off | error | warn | info | verbose` – use `verbose` for deep debugging.
|
||||||
|
|
||||||
|
### `vueCompilerOptions`
|
||||||
|
|
||||||
|
`tsconfig.json` / `jsconfig.json` supports:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": { /* TS options */ },
|
||||||
|
"vueCompilerOptions": {
|
||||||
|
"target": 3, // Vue compiler target
|
||||||
|
"plugins": ["@vue-macros/volar"],
|
||||||
|
"experimentalCompatMode": 2,
|
||||||
|
"data": {
|
||||||
|
"useDefineModel": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Consult the official `@vue/language-core` docs for every `vueCompilerOptions` flag; key ones include:
|
||||||
|
|
||||||
|
- `target`: Vue version (2, 3, 3.3, etc.) – affects template compilation.
|
||||||
|
- `plugins`: list of compiler plugins (macros, transform experiments).
|
||||||
|
- `experimentalCompatMode`: compatibility with legacy APIs.
|
||||||
|
- `data.useDefineModel`: toggles `<script setup> defineModel()` support.
|
||||||
|
|
||||||
|
### Custom Configuration via `server.configurations`
|
||||||
|
|
||||||
|
Language servers can watch client settings:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const config = await server.configurations.get<YourSchema>('yourSection');
|
||||||
|
server.configurations.onDidChange(applyConfig);
|
||||||
|
```
|
||||||
|
|
||||||
|
Define a JSON schema and share it with users:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"yourSection": {
|
||||||
|
"schemaBaseUrl": "./schemas",
|
||||||
|
"diagnostics": {
|
||||||
|
"severity": "warning",
|
||||||
|
"rules": {
|
||||||
|
"missing-prop": "error",
|
||||||
|
"unused-slot": "hint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Topologies
|
||||||
|
|
||||||
|
### Single `tsconfig` Project
|
||||||
|
|
||||||
|
- Standard Vue CLI / Vite projects.
|
||||||
|
- Volar loads the root `tsconfig.json` (or `jsconfig.json`) and watches the file tree beneath it.
|
||||||
|
- Keep `include`/`exclude` synchronized with your actual files to avoid invisible components.
|
||||||
|
|
||||||
|
### Multi-Root Workspace (VS Code)
|
||||||
|
|
||||||
|
- Each folder has its own `tsconfig.json`.
|
||||||
|
- Volar runs a separate project per folder; cross-folder references use TypeScript project references if configured.
|
||||||
|
- Use `.code-workspace` files to ensure consistent settings per folder:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"folders": [{ "path": "packages/app" }, { "path": "packages/admin" }],
|
||||||
|
"settings": {
|
||||||
|
"volar.takeOverMode.enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monorepo with Many `tsconfig`s
|
||||||
|
|
||||||
|
Patterns:
|
||||||
|
|
||||||
|
1. **Project References** – preferred for TypeScript-heavy repos.
|
||||||
|
- Root `tsconfig.json` lists `references` to each package.
|
||||||
|
- Run `tsc --build` to validate; Volar mirrors the structure for editor flows.
|
||||||
|
2. **Workspace Globs** – if you have dozens of packages, create a script to generate a “super tsconfig” that includes every `tsconfig.*.json`.
|
||||||
|
3. **Per-package configs** – configure the LS to watch each package (Neovim `volar` plugin allows multiple root patterns).
|
||||||
|
|
||||||
|
**Best Practices:**
|
||||||
|
|
||||||
|
- Keep `tsconfig.json` names consistent (use `tsconfig.app.json`, `tsconfig.lib.json`).
|
||||||
|
- Enable `"composite": true` for referenced projects so TS + Volar can resolve type information quickly.
|
||||||
|
- Avoid circular references; Volar mirrors TypeScript’s behavior and will emit similar diagnostics if references loop.
|
||||||
|
|
||||||
|
### Custom Workspaces (Outside TS)
|
||||||
|
|
||||||
|
If you’re building an LSP that doesn’t rely on `tsconfig`, manage workspaces manually:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
server.workspaceFolders.onDidChange(({ added, removed }) => {
|
||||||
|
for (const folder of added) loadProject(folder);
|
||||||
|
for (const folder of removed) unloadProject(folder);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- `loadProject` can build an in-memory graph (files, schema lookups, etc.) tailored to your tooling.
|
||||||
|
- Always respect `server.initializeParams.workspaceFolders` on startup.
|
||||||
|
|
||||||
|
## Take Over Mode vs Non-Take Over Mode
|
||||||
|
|
||||||
|
| Mode | Pros | Cons | When to Use |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Take Over Mode enabled (`volar.takeOverMode.enabled = true`) | One TypeScript server handles `.ts/.js/.vue` so template types flow into scripts seamlessly. | Requires disabling built-in TS server; some lightweight editors don’t support it. | Default for VS Code users building Vue apps with TS. |
|
||||||
|
| Take Over Mode disabled | Built-in TS handles `.ts/.js`, Volar handles `.vue`. | Script + template types are disconnected; editing `.ts` may not reflect template data. | Pure JavaScript projects, or when the host editor can’t relinquish control of `tsserver`. |
|
||||||
|
|
||||||
|
**Neovim / Sublime / Other Editors:** expose a setting for users to choose. Document the trade-offs clearly (e.g., “Enable Take Over Mode if you want `<script setup>` types to flow into `.ts` files, but note that TypeScript diagnostics now come from Volar’s server”).
|
||||||
|
|
||||||
|
## Settings Synchronization
|
||||||
|
|
||||||
|
### VS Code
|
||||||
|
|
||||||
|
- Use `workspace.getConfiguration('volar')` in extensions to read settings.
|
||||||
|
- Volar automatically watches `DidChangeConfiguration` and exposes it via `server.configurations`.
|
||||||
|
|
||||||
|
### Neovim
|
||||||
|
|
||||||
|
- Most LSP clients support the `workspace/didChangeConfiguration` notification. Ensure your plugin forwards user settings (Lua table) to Volar.
|
||||||
|
|
||||||
|
### CLI / Headless
|
||||||
|
|
||||||
|
- For custom CLIs, supply configuration as JSON via `connection.initializeParams.initializationOptions`.
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
startLanguageServer(async (params) => ({
|
||||||
|
languageService: createService(params.initializationOptions),
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Feature Toggles & Profiles
|
||||||
|
|
||||||
|
Consider offering profiles for different workflows:
|
||||||
|
|
||||||
|
| Profile | Settings |
|
||||||
|
| --- | --- |
|
||||||
|
| “Strict typing” | `diagnostics.severity = error`, enable template type checks, run `takeOverMode`. |
|
||||||
|
| “Draft mode” | `diagnostics.onChange = false`, take over mode off, minimal info. |
|
||||||
|
| “Docs playground” | Disable heavy TS features, turn on `workspace.diagnostics` for quick summary output. |
|
||||||
|
|
||||||
|
Expose these as configuration “presets” so teams can switch quickly (e.g., `volar.profile = "strict"`).
|
||||||
|
|
||||||
|
## Practical Tips
|
||||||
|
|
||||||
|
1. **Document defaults** – when adding `server.configurations` keys, publish a table showing default values and range of allowed inputs.
|
||||||
|
2. **Validate settings** – run user-provided config through a JSON schema; reject invalid values and emit telemetry/log entries.
|
||||||
|
3. **Version config changes** – support `configVersion` so older clients can detect incompatible settings and fall back gracefully.
|
||||||
|
4. **Surface config errors** – use `connection.window.showWarningMessage` to notify users when configuration fails to apply (missing schema files, invalid globs, etc.).
|
||||||
|
5. **Per-folder overrides** – respect the `scopeUri` parameter in `server.configurations.get(section, scopeUri)` so multi-root workspaces can set different options per folder.
|
||||||
|
|
||||||
|
With these patterns, Volar can scale from simple single-project setups to sprawling monorepos, while giving both maintainers and end-users predictable levers to tune behavior. Always document the knobs you expose, and test configuration changes across VS Code, Neovim, and CLI flows to ensure settings propagate consistently.
|
||||||
65
docs/cross-language-recipes.md
Normal file
65
docs/cross-language-recipes.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Cross-Language Recipes (Astro, Svelte, Marko & more)
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Source Map & Code Gen](source-map-and-code-gen.md)
|
||||||
|
|
||||||
|
Volar’s architecture is not limited to Vue. This guide outlines how teams have adapted Volar’s language-service stack to other component dialects such as Astro, Svelte, and Marko, plus concrete examples of the required plugins and configuration.
|
||||||
|
|
||||||
|
## Astro
|
||||||
|
|
||||||
|
Astro combines multiple frameworks in `.astro` files. The Astro team built [`astro-language-tools`](https://github.com/withastro/language-tools), which reuses Volar concepts.
|
||||||
|
|
||||||
|
### Key Ideas
|
||||||
|
|
||||||
|
- Implement a language plugin that parses `.astro` files and produces virtual TypeScript modules for frontmatter, component scripts, and template expressions.
|
||||||
|
- Expose Astro-specific transforms through `@volar/language-core`.
|
||||||
|
- Register custom Volar services for diagnostics (e.g., checking `Astro.props` usage).
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
|
||||||
|
- [astro-language-tools](https://github.com/withastro/language-tools) repo (look at `pkg/language-server` for Volar integration).
|
||||||
|
- `astro` VS Code extension uses the Volar-style LSP plus additional analyzers.
|
||||||
|
|
||||||
|
## Svelte
|
||||||
|
|
||||||
|
Svelte has its own LSP, but you can reuse Volar patterns to extend it or embed Svelte files in hybrid projects.
|
||||||
|
|
||||||
|
### Hybrid Example (Vue + Svelte)
|
||||||
|
|
||||||
|
- Use `nvim-lspconfig` or VS Code multi-root settings to run both the Svelte LSP and Volar simultaneously.
|
||||||
|
- For shared tooling (e.g., CLI diagnostics), build a language plugin that treats `.svelte` similarly to `.vue`, generating TS code via Svelte’s compiler.
|
||||||
|
- Reference: [sveltejs/language-tools](https://github.com/sveltejs/language-tools). Their `svelte2tsx` pipeline mirrors Volar’s `code-gen` approach.
|
||||||
|
|
||||||
|
## Marko
|
||||||
|
|
||||||
|
[Marko](https://markojs.com/) (eBay) uses `.marko` files that compile to HTML + JS.
|
||||||
|
|
||||||
|
- Implement a language plugin that compiles Marko templates to TypeScript using Marko’s compiler.
|
||||||
|
- Use `@volar/typescript` to expose the generated TS to Volar’s language service.
|
||||||
|
- Provide custom diagnostics for Marko-specific syntax; reuse `volar-service-html` for HTML-level completions.
|
||||||
|
|
||||||
|
## Tailwind / CSS frameworks
|
||||||
|
|
||||||
|
- Install `volar-service-css` and community Tailwind plugins (`@volar-plugins/tailwind`). Configure globs for `tailwind.config.js`.
|
||||||
|
- For multi-language files (e.g., `.astro` + Tailwind), ensure your custom language plugin marks embedded `<style>` blocks so the CSS service can attach.
|
||||||
|
|
||||||
|
## Markdown + Vue (MDX-style)
|
||||||
|
|
||||||
|
- Use custom language plugins to parse `.md`/`.mdx` and extract fenced Vue blocks.
|
||||||
|
- Register `extensions: ["vue", "md"]` in `vueCompilerOptions`.
|
||||||
|
- Example: VitePress and VuePress use Volar internally to provide IntelliSense inside markdown code fences.
|
||||||
|
|
||||||
|
## Practical Steps for Any Language
|
||||||
|
|
||||||
|
1. **Parse Source** – Use the language’s compiler/AST to split out scripts/templates/styles.
|
||||||
|
2. **Emit Virtual TS/HTML** – Leverage `@volar/code-gen` to map source segments to generated code.
|
||||||
|
3. **Hook TypeScript** – Configure `@volar/typescript` so TypeScript sees the generated files.
|
||||||
|
4. **Add Services** – Register diagnostic/completion services specific to the language (macros, components).
|
||||||
|
5. **Integrate Editors** – Use `@volar/kit` for LSP, `@volar/monaco` for playgrounds, `@volar/editor` for custom IDEs.
|
||||||
|
|
||||||
|
## Example Repositories
|
||||||
|
|
||||||
|
- [astro-language-tools](https://github.com/withastro/language-tools) – Astro + Volar integration.
|
||||||
|
- [windicss/windicss-intellisense](https://github.com/windicss/windicss-intellisense) – Tailwind-style completions built as Volar services.
|
||||||
|
- [sxzz/vue-macros](https://github.com/sxzz/vue-macros/tree/main/packages/volar) – Macros across templates; good reference for cross-cutting features.
|
||||||
|
|
||||||
|
By following these patterns, you can bring Volar’s robust TypeScript-driven tooling to any component format—just adapt the language plugin/code-gen layer, then reuse the existing language-service infrastructure.***
|
||||||
82
docs/custom-diagnostics.md
Normal file
82
docs/custom-diagnostics.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Authoring Custom Diagnostics with Volar
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
This recipe shows how to add bespoke diagnostics to Volar—enforcing naming conventions, linting custom directives, or validating config keys—while keeping behavior consistent across editors and CLI.
|
||||||
|
|
||||||
|
## Step 1: Language-Service Plugin Skeleton
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { LanguageServicePlugin } from '@volar/language-service';
|
||||||
|
|
||||||
|
export function createCustomDiagnosticsPlugin(opts: { ruleName: string }): LanguageServicePlugin {
|
||||||
|
return {
|
||||||
|
name: 'custom-diagnostics',
|
||||||
|
capabilities: { diagnosticProvider: true },
|
||||||
|
create(context) {
|
||||||
|
return {
|
||||||
|
async provideDiagnostics(uri) {
|
||||||
|
const doc = context.documents.get(uri);
|
||||||
|
if (!doc) return [];
|
||||||
|
const diagnostics = [];
|
||||||
|
|
||||||
|
const text = doc.getText();
|
||||||
|
for (const match of text.matchAll(/TODO/g)) {
|
||||||
|
diagnostics.push({
|
||||||
|
source: opts.ruleName,
|
||||||
|
message: 'TODO found',
|
||||||
|
severity: 2,
|
||||||
|
range: {
|
||||||
|
start: doc.positionAt(match.index!),
|
||||||
|
end: doc.positionAt(match.index! + match[0].length),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return diagnostics;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Register in Server & CLI
|
||||||
|
|
||||||
|
- Add the plugin to your language-service creation logic (see [Plugin Authoring](plugin-authoring.md)).
|
||||||
|
- Ensure CLI tooling reuses the same service so diagnostics match between editor and CI.
|
||||||
|
|
||||||
|
## Step 3: Configuration Hooks
|
||||||
|
|
||||||
|
Expose rule configuration via `server.configurations`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type CustomDiagConfig = {
|
||||||
|
forbidTodos?: boolean;
|
||||||
|
severity?: 1 | 2 | 3 | 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
const plugin = createCustomDiagnosticsPlugin({
|
||||||
|
ruleName: 'forbid-todo',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Reload the plugin (or pass updated options) when configuration changes.
|
||||||
|
|
||||||
|
## Step 4: Testing
|
||||||
|
|
||||||
|
1. Unit-test `provideDiagnostics` with mocked documents.
|
||||||
|
2. Run integration tests via LSP harness (`textDocument/publishDiagnostics`).
|
||||||
|
3. Execute CLI tests to verify exit codes and output formatting.
|
||||||
|
|
||||||
|
## Step 5: Workspace Diagnostics
|
||||||
|
|
||||||
|
If your server uses `collectDiagnostics(document)` for workspace diagnostics, custom rules come along for free. Ensure each diagnostic has a unique `source` to help users filter.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- **Performance**: Avoid expensive operations inside diagnostics; cache ASTs where possible.
|
||||||
|
- **Severity control**: Allow users to downgrade severity (`warning` vs `error`).
|
||||||
|
- **Docs**: Document rule IDs and how to disable them (per-file comments, config flags).
|
||||||
|
- **CLI flags**: Provide `--max-warnings` or `--rules custom.json` for automated pipelines.
|
||||||
|
|
||||||
|
Following this pattern makes it easy to extend Volar’s diagnostics with project-specific rules that work everywhere your tooling runs.
|
||||||
111
docs/editor-integration-alt.md
Normal file
111
docs/editor-integration-alt.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# Integrating Volar with Alternative Editors
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Live Examples](live-examples.md) • [Volar Kit & Editor](volar-kit-and-editor.md)
|
||||||
|
|
||||||
|
Volar ships an LSP server, but every editor has its own packaging and configuration needs. This guide outlines how to bundle Volar inside JetBrains IDEs, Sublime Text, Neovim, Emacs, and other environments so users get first-class Vue tooling everywhere.
|
||||||
|
|
||||||
|
## JetBrains IDEs (WebStorm, IntelliJ IDEA)
|
||||||
|
|
||||||
|
JetBrains IDEs don’t use LSP internally, but you can still reuse Volar’s language service via the IDE plugin API.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
1. **Leverage the built-in Vue plugin** (recommended) – JetBrains already bundles Vue-aware tooling inspired by Volar.
|
||||||
|
2. **Embed Volar via a custom plugin** – advanced; wrap Volar using the [com.intellij.lang.javascript](https://plugins.jetbrains.com/docs/intellij/javascript.html) API.
|
||||||
|
|
||||||
|
#### Embedding Steps
|
||||||
|
|
||||||
|
- Package the Volar language service as a Node process inside your plugin.
|
||||||
|
- Use the JetBrains LSP Support plugin (2023.2+) to spawn the Volar server and bridge diagnostics/completions into the IDE.
|
||||||
|
- Configure CLI path + initialization options via plugin settings.
|
||||||
|
|
||||||
|
Key Settings:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val command = listOf("node", "/path/to/volar/server.js", "--stdio")
|
||||||
|
val lspServer = ProcessHandler(command, project.basePath)
|
||||||
|
```
|
||||||
|
|
||||||
|
Expose options for Take Over Mode, log level, schema paths. Document how to install Node/TS dependencies (or bundle them).
|
||||||
|
|
||||||
|
## Sublime Text (LSP-volar)
|
||||||
|
|
||||||
|
1. Install [LSP](https://packagecontrol.io/packages/LSP) and [LSP-volar](https://github.com/sublimelsp/LSP-volar).
|
||||||
|
2. Settings override example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volar.takeOverMode.enabled": true,
|
||||||
|
"volar.tsPlugin": true,
|
||||||
|
"typescript.tsdk": "/usr/local/lib/node_modules/typescript/lib"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Expose user settings for:
|
||||||
|
|
||||||
|
- Node path.
|
||||||
|
- `tsconfig` discovery (multi-root).
|
||||||
|
- Take Over Mode toggle.
|
||||||
|
|
||||||
|
## Neovim
|
||||||
|
|
||||||
|
Use `nvim-lspconfig` or `volar.nvim`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require'lspconfig'.volar.setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
init_options = {
|
||||||
|
typescript = {
|
||||||
|
tsdk = '/path/to/node_modules/typescript/lib'
|
||||||
|
},
|
||||||
|
languageFeatures = {
|
||||||
|
implementation = true,
|
||||||
|
references = true,
|
||||||
|
documentHighlight = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Best Practices:
|
||||||
|
|
||||||
|
- Document how to disable the built-in TypeScript server when Take Over Mode is active (`tsserver = false`).
|
||||||
|
- Provide Lua commands (`:VolarRestart`, `:VolarToggleTakeOver`) to adjust settings without restarting Neovim.
|
||||||
|
|
||||||
|
## Emacs
|
||||||
|
|
||||||
|
`lsp-mode` configuration:
|
||||||
|
|
||||||
|
```elisp
|
||||||
|
(require 'lsp-volar)
|
||||||
|
(setq lsp-volar-take-over-mode t
|
||||||
|
lsp-volar-ts-server-path "/path/to/typescript/lib"
|
||||||
|
lsp-volar-completion-default-attr "prop")
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable relevant capabilities:
|
||||||
|
|
||||||
|
- `lsp-volar-take-over-mode`
|
||||||
|
- `lsp-volar-typescript-skip-lib-check`
|
||||||
|
- Workspace folders for monorepos.
|
||||||
|
|
||||||
|
## Custom Electron/Native Editors
|
||||||
|
|
||||||
|
1. Use `@volar/editor` to embed the language service directly.
|
||||||
|
2. Maintain a background worker that mirrors LSP features (diagnostics, completion).
|
||||||
|
3. Provide developer tools (log viewer, restart button, config UI).
|
||||||
|
|
||||||
|
## VS Code Web / Codespaces
|
||||||
|
|
||||||
|
- Bundle the Volar server compiled to WASM/Node (depending on environment).
|
||||||
|
- Ensure dependencies (TypeScript, plugins) are pre-installed in the devcontainer or Codespaces image.
|
||||||
|
- Expose settings via `.devcontainer/devcontainer.json` to enable Take Over Mode by default.
|
||||||
|
|
||||||
|
## Distribution Tips
|
||||||
|
|
||||||
|
- **Binary bundling**: package server files with your extension/plugin; avoid requiring global installs.
|
||||||
|
- **Configuration UI**: give users toggles for log level, Take Over Mode, TS SDK path, schema settings.
|
||||||
|
- **Docs**: maintain README sections for each editor with exact install commands and troubleshooting steps (common errors: missing Node, duplicate TS servers).
|
||||||
|
- **Updates**: automate dependency upgrades (TypeScript, Volar) via bots to keep integrations current.
|
||||||
|
|
||||||
|
By tailoring Volar’s configuration for each editor, you deliver consistent Vue DX whether users prefer VS Code, Neovim, Sublime, JetBrains, or custom tooling. Use the live examples as blueprints and keep documentation close so users can self-service common issues.
|
||||||
133
docs/error-handling-and-resilience.md
Normal file
133
docs/error-handling-and-resilience.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Error Handling & Resilience Playbook
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Telemetry](telemetry-and-observability.md) • [UX Best Practices](ux-best-practices.md)
|
||||||
|
|
||||||
|
Language servers run continuously inside users’ editors and CI pipelines. Robust error handling keeps them stable even when schemas fail to download, configuration is malformed, or files vanish mid-request. This guide catalogs every failure mode we’ve encountered and how to mitigate it gracefully.
|
||||||
|
|
||||||
|
## Guiding Principles
|
||||||
|
|
||||||
|
1. **Fail soft** – surface actionable warnings, but keep the server alive.
|
||||||
|
2. **Fallbacks everywhere** – default to cached schemas, previous configuration, or safe defaults.
|
||||||
|
3. **Short-circuit** – bail out early when prerequisites (schema, file content) aren’t available rather than cascading errors.
|
||||||
|
|
||||||
|
## Common Failure Modes & Mitigations
|
||||||
|
|
||||||
|
### Schema Fetch Failures
|
||||||
|
|
||||||
|
Symptoms: diagnostics missing, completion items blank, repeated network errors.
|
||||||
|
|
||||||
|
Mitigations:
|
||||||
|
|
||||||
|
- Cache schemas on disk (`~/.cache/volar-schemas`) or in memory (`Map`).
|
||||||
|
- Retry with exponential backoff; after a few retries, fall back to last known schema.
|
||||||
|
- Log via `connection.console.warn`, notify the user if failure persists.
|
||||||
|
- Allow users to disable remote fetches (`config.schemas.allowRemote = false`) for air-gapped environments.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function fetchSchema(uri: string) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(uri);
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
return await response.text();
|
||||||
|
} catch (err) {
|
||||||
|
log('warn', 'Schema fetch failed', { uri, err: String(err) });
|
||||||
|
return cache.get(uri) ?? DEFAULT_SCHEMA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Missing Files / Race Conditions
|
||||||
|
|
||||||
|
When editors delete or move files while diagnostics are running:
|
||||||
|
|
||||||
|
- Re-fetch the document from `server.documents` right before publishing diagnostics, and ensure the version matches.
|
||||||
|
- Wrap file reads in `try/catch` and skip processing if the file disappears.
|
||||||
|
|
||||||
|
### Invalid User Configuration
|
||||||
|
|
||||||
|
If users supply malformed settings:
|
||||||
|
|
||||||
|
- Validate against a JSON schema; on failure, log and revert to defaults.
|
||||||
|
- Notify users via `connection.window.showErrorMessage` (“Invalid `volarJsonYaml.schemas`: expected array, received string”).
|
||||||
|
- Never crash the server due to config errors.
|
||||||
|
|
||||||
|
### TypeScript Project Reloads
|
||||||
|
|
||||||
|
- Guard `project.reload()` with a debounce; rapid reloads can starve the event loop.
|
||||||
|
- Catch errors from TypeScript’s API and surface them with actionable messaging (e.g., “Failed to read tsconfig.json: …”).
|
||||||
|
|
||||||
|
### Plugin Exceptions
|
||||||
|
|
||||||
|
- Wrap plugin hooks in `try/catch` to prevent a single plugin from taking down the server.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function safeInvoke<T>(fn: () => T, fallback: T) {
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} catch (err) {
|
||||||
|
log('error', 'Plugin error', { err: String(err) });
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Pressure
|
||||||
|
|
||||||
|
- Dispose snapshots (`snapshot.dispose?.()`) when removing virtual files.
|
||||||
|
- Avoid keeping entire project graphs in memory—rely on Volar’s internal caches.
|
||||||
|
- Expose a `volar/clearCaches` command for debugging sessions; reset caches when memory spikes.
|
||||||
|
|
||||||
|
## Fallback Configuration Strategy
|
||||||
|
|
||||||
|
1. Load default config.
|
||||||
|
2. Merge user config (`server.configurations`).
|
||||||
|
3. Validate; if validation fails, log error and keep defaults.
|
||||||
|
4. Apply new config atomically (e.g., swap references to schema caches).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function applyConfiguration(rawConfig: unknown) {
|
||||||
|
const parsed = schema.safeParse(rawConfig);
|
||||||
|
if (!parsed.success) {
|
||||||
|
log('warn', 'Invalid config, using defaults', { issues: parsed.error.issues });
|
||||||
|
return defaultConfig;
|
||||||
|
}
|
||||||
|
return { ...defaultConfig, ...parsed.data };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Editor Recovery Patterns
|
||||||
|
|
||||||
|
### VS Code
|
||||||
|
|
||||||
|
- Use `connection.languages.diagnostics.refresh` after recovering from an error (e.g., schema server returns to service) to prompt the client for fresh diagnostics.
|
||||||
|
- Provide a command “Reload Volar Server” that restarts the process if the user wants a clean slate.
|
||||||
|
|
||||||
|
### Neovim / CLI
|
||||||
|
|
||||||
|
- Document a `:VolarRestart` command or CLI flag (`volar --restart`) to drop caches.
|
||||||
|
- If the server exits due to an unrecoverable error, ensure the client auto-restarts it with limited retries to avoid infinite loops.
|
||||||
|
|
||||||
|
## Testing Resilience
|
||||||
|
|
||||||
|
1. Simulate network failures by mocking `fetch` to throw errors.
|
||||||
|
2. Provide fixtures with malformed configuration and assert the server falls back to defaults without crashing.
|
||||||
|
3. Force `server.documents.get` to return `undefined` mid-request to confirm handlers bail out gracefully.
|
||||||
|
4. Run stress tests opening/closing hundreds of files to ensure watchers and caches don’t leak.
|
||||||
|
|
||||||
|
## Monitoring & Alerts
|
||||||
|
|
||||||
|
- Emit telemetry for repeated failures (5 schema fetch failures in a row) and consider surfacing a notification prompting the user to check their network or configuration.
|
||||||
|
- Track server restarts if the host environment allows; multiple restarts in quick succession indicate a crash loop.
|
||||||
|
|
||||||
|
## Quick Reference Checklist
|
||||||
|
|
||||||
|
- [ ] Cache schemas and support offline mode.
|
||||||
|
- [ ] Validate configuration and fall back to defaults on errors.
|
||||||
|
- [ ] Guard plugin hooks and log exceptions.
|
||||||
|
- [ ] Compare document versions before publishing diagnostics.
|
||||||
|
- [ ] Dispose snapshots and purge caches when documents close.
|
||||||
|
- [ ] Provide commands or RPC hooks to restart/clear caches.
|
||||||
|
- [ ] Surface actionable notifications for persistent issues.
|
||||||
|
- [ ] Cover failure scenarios in tests (network, config, missing files).
|
||||||
|
|
||||||
|
Designing for resilience upfront saves countless hours later. Treat every external dependency (network, file system, user config) as unreliable, and make fallbacks explicit so your language server remains stable under real-world conditions.
|
||||||
157
docs/getting-started.md
Normal file
157
docs/getting-started.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# VolarJS Getting Started
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Source Map & Code Gen](source-map-and-code-gen.md) • [Configuration & Projects](configuration-and-projects.md)
|
||||||
|
|
||||||
|
VolarJS is the reference implementation of Vue Language Server tooling. Its packages are intentionally small so you can wire only what you need—whether that is a full LSP server, a web-based editor integration, or a bespoke CLI that runs Vue-aware transforms. This guide explains the moving pieces, how they relate, and the quickest path to seeing language features light up.
|
||||||
|
|
||||||
|
## Mental Model
|
||||||
|
|
||||||
|
```
|
||||||
|
Vue source files ──▶ @volar/language-core ──▶ @volar/language-service plugins ──▶ Host adapters (LSP, VS Code, Monaco, custom)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **File understanding** happens in `@volar/language-core`. It parses Vue SFCs, tracks virtual files, and creates source maps between template/script/style sections.
|
||||||
|
2. **Language features** live in `@volar/language-service` (hover, completion, diagnostics, etc.) and are provided by plugins. Vue’s default feature set is one plugin; you can add more.
|
||||||
|
3. **Adapters** such as `@volar/language-server` or `@volar/monaco` connect the language service to an IDE, editor, or any host that speaks a protocol.
|
||||||
|
|
||||||
|
## Package Overview
|
||||||
|
|
||||||
|
### Core Building Blocks
|
||||||
|
|
||||||
|
| Package | What it does | When to depend on it |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `@volar/source-map` | Bidirectional source-map abstraction used everywhere Volar needs to point back into the user’s Vue/TS files. Supports stacked maps (template <-> TS <-> emitted JS). | Useful whenever you create custom virtual files or remap diagnostics. |
|
||||||
|
| `@volar/code-gen` | Utility for generating virtual TypeScript text plus accompanying source maps in lockstep. The Vue plugin uses it to describe script/template output. | Authoring custom language-core plugins or transforms. |
|
||||||
|
| `@volar/language-core` | Creates the document graph, manages embedded files, and wires `source-map` + `code-gen` so features understand Vue SFC structure. | Always required; everything else builds on the core. |
|
||||||
|
| `@volar/typescript` | Wraps the TS compiler API, exposing helpers to build/refresh programs, track script snapshots, and share language service hosts. | Needed whenever you want TS-powered diagnostics or IntelliSense (nearly all workflows). |
|
||||||
|
|
||||||
|
### Service Layer
|
||||||
|
|
||||||
|
| Package | What it does | When to depend on it |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `@volar/language-service` | Orchestrates feature plugins (hover, completion, code actions, etc.) and exposes the API a host will call. | Required for any integration responding to language requests. |
|
||||||
|
| `@volar/vue-language-core` | Vue-specific implementation of language-core plugins (parses `.vue` blocks, tracks template regions, emits TS code). | Needed if you want first-party Vue behavior without reinventing parsing. |
|
||||||
|
| `@volar/vue-language-service` | Bundled set of language-service plugins delivering Vue-aware completions, diagnostics, refactors, formatting, etc. | Drop-in when you just need the standard Vue feature set. |
|
||||||
|
| `@volar/vue-typescript` | Provides a TypeScript plugin so TS itself understands `.vue` files (Take Over Mode, `vue-tsc`). | Required if you let TypeScript drive the project graph or run `vue-tsc`. |
|
||||||
|
| `volar-service-*` plugins | Feature add-ons (Prettier formatting, CSS frameworks, component libraries). Each exposes hooks for both core and language-service layers. | Opt-in when the default Vue plugin is not enough. |
|
||||||
|
|
||||||
|
#### Common `volar-service-*` Add-ons
|
||||||
|
|
||||||
|
| Plugin | Capability | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `volar-service-prettier` | Formatting for templates + scripts via Prettier. | Configure formatter options via `initializationOptions.plugins.prettier`. |
|
||||||
|
| `volar-service-css` | CSS/SCSS completions, diagnostics, folding. | Injects style virtual documents; customizable with `css.languages`. |
|
||||||
|
| `volar-service-html` | HTML attribute hints, tag completions for template sections. | Useful for custom component libs that mirror HTML behavior. |
|
||||||
|
| `volar-service-typescript-twoslash-queries` | Inline `// ^?` query evaluation for debugging type state. | Great for docs/playground experiences. |
|
||||||
|
| `volar-service-emmet` | Emmet abbreviations inside Vue templates. | Enable selectively per workspace to avoid conflicts. |
|
||||||
|
|
||||||
|
All services share the same `LanguageServicePlugin` interface, so you can inspect these packages as reference implementations when building your own feature modules.
|
||||||
|
|
||||||
|
### Adapter / Host Packages
|
||||||
|
|
||||||
|
| Package | What it does | When to depend on it |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `@volar/language-server` | Wraps the language service with an LSP transport (3.17), file watching, and JSON-RPC plumbing. | Editors that speak LSP (VS Code, Neovim, Sublime LSP, etc.). |
|
||||||
|
| `@volar/kit` | Convenience bootstrapper for CLIs and custom hosts; bundles configs, file watching, and plugin wiring helpers. | Rapidly wiring a bespoke integration or experimentation harness. |
|
||||||
|
| `@volar/editor` | Provides thin abstractions around text documents, connections, and workspace edits so non-LSP hosts can embed Volar with less boilerplate. | Custom IDEs or web editors that do not rely on Monaco. |
|
||||||
|
| `@volar/monaco` | Binds the language service to Monaco Editor APIs (workers, markers, completion providers). | Browser-based playgrounds, docs sandboxes, or VS Code Web-style apps. |
|
||||||
|
|
||||||
|
> Tip: “VolarJS” refers to the overall toolkit. Individual packages keep the `@volar/` scope, core/service helpers focus on composition, and feature plugins often start with `volar-service-`.
|
||||||
|
|
||||||
|
## Installation Cheatsheet
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev @volar/language-core @volar/language-service @volar/typescript
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the adapter you need:
|
||||||
|
|
||||||
|
- LSP host: `npm install --save-dev @volar/language-server`
|
||||||
|
- Web editor: `npm install --save @volar/monaco`
|
||||||
|
- Extra features: `npm install --save-dev volar-service-<name>`
|
||||||
|
|
||||||
|
## Bootstrapping the Core + Service
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// tooling/createVolarService.ts
|
||||||
|
import { createLanguageCore } from '@volar/language-core';
|
||||||
|
import { createLanguageService } from '@volar/language-service';
|
||||||
|
import { createTypeScriptProject } from '@volar/typescript';
|
||||||
|
import vuePlugin from '@volar/vue-language-service'; // ships inside the main repo
|
||||||
|
|
||||||
|
export function createVolarService(tsconfigPath: string) {
|
||||||
|
const tsProject = createTypeScriptProject(tsconfigPath);
|
||||||
|
const core = createLanguageCore({
|
||||||
|
plugins: [vuePlugin.tsPlugin],
|
||||||
|
ts: tsProject.typescript,
|
||||||
|
getScriptContent: tsProject.host.readFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
return createLanguageService(core, {
|
||||||
|
plugins: [vuePlugin.languageService],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Core setup** wires TypeScript and Vue-specific transforms.
|
||||||
|
- **Language service** registers plugins. Each plugin may expose both a TypeScript layer (virtual files) and an LSP layer (features).
|
||||||
|
- Under the hood, the Vue plugin relies on `@volar/code-gen` + `@volar/source-map` to keep every generated byte mapped back to the user’s SFC so diagnostics and quick fixes land in the right spot.
|
||||||
|
|
||||||
|
## Common Integration Paths
|
||||||
|
|
||||||
|
### 1. Shipping an LSP Server (VS Code, Neovim, Sublime)
|
||||||
|
|
||||||
|
1. Install `@volar/language-server`.
|
||||||
|
2. Create an `entry.ts` that calls `createLanguageServer` (or use `@volar/kit`’s CLI helpers) and forwards stdio (or node IPC) to the editor.
|
||||||
|
3. Provide configuration hooks so users can adjust `tsconfig`, Take Over Mode, and plugin toggles.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { startLanguageServer } from '@volar/language-server/node';
|
||||||
|
import { createVolarService } from './tooling/createVolarService';
|
||||||
|
|
||||||
|
startLanguageServer(async (params) => ({
|
||||||
|
languageService: createVolarService(params.initializationOptions.tsconfig),
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Embedding in a Browser Playground (Monaco)
|
||||||
|
|
||||||
|
1. Install `@volar/monaco`.
|
||||||
|
2. Register workers and configure virtual file resolution (fetch files over HTTP, from memory, etc.).
|
||||||
|
3. Attach the Volar language service to Monaco’s `languages.register` API.
|
||||||
|
4. For fully custom editors, use `@volar/editor` to avoid wiring the text-buffer plumbing from scratch.
|
||||||
|
|
||||||
|
### 3. Running Volar Inside a CLI
|
||||||
|
|
||||||
|
1. Build the service as shown earlier (or bootstrap via `@volar/kit` which wires file watching + reloads).
|
||||||
|
2. Call methods like `doValidation`, `findReferences`, or `format` on demand.
|
||||||
|
3. Perfect for codemods, lint rules, or CI checks that require Vue awareness without firing up an editor.
|
||||||
|
|
||||||
|
## How the Pieces Fit
|
||||||
|
|
||||||
|
- `@volar/language-core` owns **document truth** (what files exist, how they map).
|
||||||
|
- `@volar/code-gen` and `@volar/source-map` make sure generated TS/JS stays tied to the user’s original locations.
|
||||||
|
- `@volar/typescript` keeps Volar in sync with TS’s program graph.
|
||||||
|
- `@volar/language-service` exposes **capabilities** and loads **plugins**.
|
||||||
|
- Vue-specific packages (`@volar/vue-language-core`, `@volar/vue-language-service`, `@volar/vue-typescript`) ship the batteries-included feature set.
|
||||||
|
- Adapter packages (LSP, Monaco, Editor, Kit) connect those capabilities to a host protocol or runtime.
|
||||||
|
- Plugin packages (prefixed `volar-service-`) extend capabilities for CSS frameworks, formatting, component libraries, etc.
|
||||||
|
|
||||||
|
Think of it as layers: Core → Service → Adapter. When you extend Volar, you usually write a plugin for the service layer; when you embed it, you pick or author an adapter.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Decide which host you are targeting (LSP editor vs browser vs CLI).
|
||||||
|
2. Scaffold a `createVolarService` helper shared across adapters.
|
||||||
|
3. Add the plugins you need (Prettier, Tailwind, custom diagnostics).
|
||||||
|
4. Document your initialization options so teams know how to tune Volar for their monorepo or tooling stack.
|
||||||
|
|
||||||
|
With these pieces in place you have a repeatable recipe for integrating any Vue-aware experience with the VolarJS ecosystem.
|
||||||
|
|
||||||
|
## Troubleshooting Cheatsheet
|
||||||
|
|
||||||
|
- **Take Over Mode conflicts** (`@volar/vue-typescript` vs plain TS LS): ensure only one TS server runs by disabling the built-in TypeScript extension (VS Code) or pointing Neovim/Sublime to the Volar TS plugin. Mixed servers lead to duplicate diagnostics.
|
||||||
|
- **`vue-tsc` misses project references**: pass the same `tsconfig.json` and `--project` path you hand to `createTypeScriptProject`; Volar piggybacks on TS incremental builds, so mismatched configs spawn duplicate programs.
|
||||||
|
- **Custom plugins not firing**: confirm they’re registered in both `language-core` (for embedded files) and `language-service` (for features). Many `volar-service-*` examples export `tsPlugin` and `languageService` to show both halves.
|
||||||
|
- **Broken source maps** after transforms: verify that every `codeGen.addText` call is paired with `codeGen.addMapping`. Leverage `@volar/source-map`’s `toGeneratedPositions` helpers to debug where a diagnostic lands.
|
||||||
|
- **High CPU in monorepos**: reach for `@volar/kit`’s file-watching filters (globs, `ignored`, `maxFileSize`) so unnecessary assets do not create snapshots. When embedding manually, reuse the same TS `DocumentRegistry`.
|
||||||
52
docs/live-examples.md
Normal file
52
docs/live-examples.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Live Projects Built on VolarJS
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md)
|
||||||
|
|
||||||
|
This catalog highlights active projects and integrations that leverage VolarJS packages today. Use it for inspiration, or to see how others structure their tooling.
|
||||||
|
|
||||||
|
## Editor Extensions
|
||||||
|
|
||||||
|
| Project | Maintainer | Volar Usage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [Vue Language Features (Volar)](https://github.com/vuejs/language-tools) | Vue.js Core Team | Official VS Code extension built on `@volar/language-server`, `@vue/language-core`, and `@volar/vue-language-service`. Powers Take Over Mode, template inference, and diagnostics. |
|
||||||
|
| [nvim-lspconfig (volar)](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#volar) | Neovim | Provides ready-made configuration for Neovim’s built-in LSP client, pointing to the Volar language server binary with Take Over Mode toggles. |
|
||||||
|
| [coc-volar](https://github.com/yaegassy/coc-volar) | yaegassy | Conquer of Completion (CoC) extension bundling `@volar/language-server` for Vim/Neovim users. |
|
||||||
|
| [LSP-volar](https://github.com/sublimelsp/LSP-volar) | SublimeLSP | Sublime Text integration that wraps the Volar server to deliver Vue diagnostics, hover, and completion support. |
|
||||||
|
| [volar.nvim](https://github.com/askfiy/volar.nvim) | askfiy | Neovim plugin that auto-manages the Volar binary, supports Take Over Mode, and exposes commands to restart or switch profiles. |
|
||||||
|
| [emacs-lsp/lsp-mode (lsp-volar)](https://emacs-lsp.github.io/lsp-mode/page/lsp-volar/) | Emacs LSP Team | Registers the Volar language server inside Emacs’ `lsp-mode`, including settings for Take Over Mode and TS plugin options. |
|
||||||
|
|
||||||
|
## Online IDEs & Playground Integrations
|
||||||
|
|
||||||
|
| Project | Maintainer | Volar Usage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [Vue SFC Playground](https://github.com/vuejs/repl) | Vue.js | Uses `@volar/monaco` to provide in-browser IntelliSense, diagnostics, and refactors inside the official playground. |
|
||||||
|
| [StackBlitz Vue starter](https://stackblitz.com/edit/vue) | StackBlitz | Loads Volar’s Monaco bindings to power Vue-aware completions and hover in the web IDE. |
|
||||||
|
| [CodeSandbox Projects (Vue)](https://codesandbox.io/p/dashboard) | CodeSandbox | Embeds the Volar Monaco integration so `.vue` files in the browser get the same capabilities as desktop editors. |
|
||||||
|
|
||||||
|
## CLI & Tooling
|
||||||
|
|
||||||
|
| Project | Maintainer | Volar Usage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [vue-tsc](https://github.com/vuejs/language-tools/tree/master/packages/tsc) | Vue.js | Command-line TypeScript checker that reuses `@vue/language-core` + `@volar/vue-typescript` for SFC-aware diagnostics/builds. |
|
||||||
|
| [Volar JSON/YAML LSP Example](../examples/json-yaml-lsp/README.md) | This repo | Demonstrates a custom server that combines Volar runtime utilities with `vscode-json-languageservice` and `yaml-language-server`. |
|
||||||
|
| [Nuxt DevTools](https://github.com/nuxt/devtools) | Nuxt | Embeds Volar’s language service pieces to power component auto-complete and diagnostics inside the browser-based devtools UI. |
|
||||||
|
| [Windi CSS IntelliSense](https://github.com/windicss/windicss-intellisense) | Windi CSS | Extends the Volar plugin ecosystem to provide class completions and diagnostics for Vue SFCs. |
|
||||||
|
| [Vitepress/Vue DX language features](https://github.com/vitepress) | Vue Ecosystem | Reuse Volar’s core packages for markdown-with-vue editing, including completions/diagnostics in MDX-like snippets. |
|
||||||
|
|
||||||
|
## Community Maintained Servers
|
||||||
|
|
||||||
|
| Project | Description | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [volar-service-prettier](https://github.com/vuejs/language-tools/tree/master/packages/%40vue/language-service) | Prettier-backed formatting plugin shipped with Volar. | Example of extending the plugin API to add stylistic features. |
|
||||||
|
| [volar-service-emmet](https://github.com/vuejs/language-tools/tree/master/packages/volar-service-emmet) | Emmet abbreviation support inside Vue templates. | Shows how to mix external providers into Volar’s completion pipeline. |
|
||||||
|
| [volar-service-typescript-twoslash-queries](https://github.com/vuejs/language-tools/tree/master/packages/volar-service-typescript-twoslash-queries) | Inline `^?` type query support (used in docs/playgrounds). | Built atop `@volar/language-service` plugin hooks. |
|
||||||
|
|
||||||
|
## Keeping the List Fresh
|
||||||
|
|
||||||
|
Know of another project built on VolarJS? Send a PR adding it here, with:
|
||||||
|
|
||||||
|
1. Repository or product link.
|
||||||
|
2. Brief description of how it leverages Volar (packages, features).
|
||||||
|
3. Maintainer attribution.
|
||||||
|
|
||||||
|
Seeing how others structure their integrations is one of the fastest ways to learn the ecosystem—use these examples as reference implementations or starting points for your own tooling.
|
||||||
150
docs/lsp-benchmarking.md
Normal file
150
docs/lsp-benchmarking.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# Benchmarking Volar-Based Language Servers
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Performance Guide](performance-and-debugging.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Reliable performance requires repeatable measurements. This guide explains how to benchmark a Volar-powered LSP—whether for regression testing, capacity planning, or comparing implementation choices. We cover synthetic microbenchmarks, realistic end-to-end scenarios, metrics to track, and tooling suggestions.
|
||||||
|
|
||||||
|
## Benchmarking Goals
|
||||||
|
|
||||||
|
1. **Latency** – response time for common requests (diagnostics, completion, hover, definition).
|
||||||
|
2. **Throughput** – how many requests per second the server can handle under load.
|
||||||
|
3. **Resource usage** – CPU, memory, and IO characteristics.
|
||||||
|
4. **Stability** – behavior under long-running or adverse conditions (massive workspaces, schema outages).
|
||||||
|
|
||||||
|
## Benchmarking Environments
|
||||||
|
|
||||||
|
| Environment | Use Case |
|
||||||
|
| --- | --- |
|
||||||
|
| Local machine | Quick iteration, microbenchmarks |
|
||||||
|
| Dedicated CI runner | Repeatable, no background processes |
|
||||||
|
| Containerized setup | Shareable configuration, reproducible |
|
||||||
|
|
||||||
|
Ensure you run benchmarks on idle machines to reduce noise (disable background indexing, close unrelated apps).
|
||||||
|
|
||||||
|
## Metrics to Capture
|
||||||
|
|
||||||
|
- **Latency (ms)**: time from request start to response.
|
||||||
|
- **P99 latency**: worst-case tail latency for each request type.
|
||||||
|
- **CPU usage (%)**: average and peaks.
|
||||||
|
- **Memory usage (MB)**: total process memory, heap snapshots.
|
||||||
|
- **Garbage Collection stats**: number/duration of GC cycles.
|
||||||
|
- **Cache hit rate**: schema cache hits, TypeScript project reuse, etc.
|
||||||
|
- **Errors per minute**: schema fetch failures, exceptions.
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
|
||||||
|
### 1. LSP Bench Harness
|
||||||
|
|
||||||
|
Use [`lsp-bench`](https://github.com/microsoft/vscode-languageserver-node/tree/main/tools/lsp-bench) or similar tools to replay LSP traces:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @vscode/lsp-bench run \
|
||||||
|
--server "node dist/server.js" \
|
||||||
|
--trace ./fixtures/sample.trace.json \
|
||||||
|
--out ./bench/results.json
|
||||||
|
```
|
||||||
|
|
||||||
|
- Prepare trace files capturing representative user sessions (diagnostics, completion, hover, rename).
|
||||||
|
- `lsp-bench` reports per-method latency, throughput, and timeouts.
|
||||||
|
|
||||||
|
### 2. Custom Harness
|
||||||
|
|
||||||
|
Write a Node script to issue requests in sequence or parallel:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createConnection } from 'vscode-languageserver/node';
|
||||||
|
|
||||||
|
async function benchmarkCompletion(docUri: string, positions: Position[]) {
|
||||||
|
const latencies: number[] = [];
|
||||||
|
for (const pos of positions) {
|
||||||
|
const start = performance.now();
|
||||||
|
await connection.sendRequest('textDocument/completion', { textDocument: { uri: docUri }, position: pos });
|
||||||
|
latencies.push(performance.now() - start);
|
||||||
|
}
|
||||||
|
return latencies;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use `performance.now()` or `process.hrtime.bigint()` for high-resolution timing.
|
||||||
|
- Run requests concurrently to test throughput (e.g., Promise.all on multiple files).
|
||||||
|
|
||||||
|
### 3. Profilers
|
||||||
|
|
||||||
|
- `node --prof` to generate V8 CPU profiles; use `node --prof-process` to inspect.
|
||||||
|
- `node --inspect` for interactive CPU/memory profiling.
|
||||||
|
|
||||||
|
### 4. OS Metrics
|
||||||
|
|
||||||
|
- macOS: Instruments, `Activity Monitor`.
|
||||||
|
- Linux: `perf`, `htop`, `pidstat`.
|
||||||
|
- Windows: Performance Monitor.
|
||||||
|
|
||||||
|
## Benchmark Scenarios
|
||||||
|
|
||||||
|
1. **Cold start**: time from process launch to first diagnostic result on a large workspace.
|
||||||
|
2. **Hot completion**: repeated `textDocument/completion` calls while typing in large files.
|
||||||
|
3. **Full diagnostics**: `workspace/diagnostic` on a monorepo.
|
||||||
|
4. **Schema outage**: simulate offline schema servers to ensure fallback paths are fast.
|
||||||
|
5. **Take Over Mode**: measure TS+Volar integration performance with `.ts/.vue` editing.
|
||||||
|
6. **Concurrent editing**: multiple documents edited simultaneously, triggering overlapping diagnostics.
|
||||||
|
|
||||||
|
## Benchmark Workflow
|
||||||
|
|
||||||
|
1. **Prepare fixtures** – real-world repositories or synthetic workloads (e.g., 1,000 `.vue` files).
|
||||||
|
2. **Record baseline** – run benchmarks on the `main` branch to capture baseline metrics.
|
||||||
|
3. **Introduce changes** – branch with your modifications.
|
||||||
|
4. **Rerun benchmarks** – identical environment, compare results.
|
||||||
|
5. **Analyze** – highlight regressions/improvements with absolute numbers and percentages.
|
||||||
|
6. **Automate** – integrate into CI to catch regressions before merging.
|
||||||
|
|
||||||
|
## Reporting
|
||||||
|
|
||||||
|
Include:
|
||||||
|
|
||||||
|
- Method: `textDocument/completion`
|
||||||
|
- Sample size: number of requests.
|
||||||
|
- Mean/P95/P99 latency.
|
||||||
|
- CPU/memory usage snapshots.
|
||||||
|
- Notes on environment (machine specs, Node version).
|
||||||
|
|
||||||
|
Example summary:
|
||||||
|
|
||||||
|
| Scenario | Baseline P95 | New P95 | Delta |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Completion (component props) | 42ms | 38ms | -9.5% |
|
||||||
|
| Hover | 31ms | 34ms | +9.7% |
|
||||||
|
| Cold diagnostics (monorepo) | 3.2s | 2.8s | -12.5% |
|
||||||
|
|
||||||
|
## Tips & Best Practices
|
||||||
|
|
||||||
|
1. **Isolate background noise** – disable incremental builds, watchers, or other processes during runs.
|
||||||
|
2. **Warm caches** – run a warm-up cycle before recording metrics to simulate typical usage.
|
||||||
|
3. **Measure tails** – averages hide spikes; always record P95/P99.
|
||||||
|
4. **Instrument in code** – log per-request durations (e.g., around `collectDiagnostics`) to correlate with client-side measurements.
|
||||||
|
5. **Version your traces** – keep benchmark traces under version control so future runs stay consistent.
|
||||||
|
6. **Automate regression detection** – fail CI if key metrics regress beyond a threshold (e.g., P95 > baseline + 20%).
|
||||||
|
7. **Consider user hardware** – run benchmarks on lower-spec machines to mimic real users (e.g., 4-core laptops).
|
||||||
|
8. **Monitor GC** – long GC pauses can inflate tail latency; capture heap snapshots and consider tuning Node’s GC flags if needed.
|
||||||
|
|
||||||
|
## Sample CI Step
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
benchmark:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run build
|
||||||
|
- run: npx @vscode/lsp-bench run --server "node dist/server.js" --trace ./bench/trace.json --out bench/results.json
|
||||||
|
- run: node scripts/compare-benchmarks.js bench/results.json bench/baseline.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The comparison script should exit non-zero if metrics exceed allowed regression thresholds.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Benchmarking isn’t a one-off task—embed it into your development lifecycle to catch regressions early and keep the Volar experience snappy. By combining realistic traces, automated harnesses, and clear reporting, you can confidently evolve your language server while preserving top-tier performance.
|
||||||
94
docs/monaco-playground.md
Normal file
94
docs/monaco-playground.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# 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.
|
||||||
134
docs/package-api-reference.md
Normal file
134
docs/package-api-reference.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# Volar Package API Reference
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Volar Kit & Editor](volar-kit-and-editor.md)
|
||||||
|
|
||||||
|
This reference summarizes the primary exports and configuration points for the most-used Volar packages. Use it as a quick lookup when wiring custom tooling.
|
||||||
|
|
||||||
|
## `@volar/editor`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createEditorConnection(languageService)` | Wraps a `LanguageService` and exposes methods mirroring LSP features (`languageFeatures`, `documents`, `commands`). Ideal for custom editors that do not use JSON-RPC. |
|
||||||
|
| `SnapshotDocument` | Text document implementation that tracks versions and provides `ts.IScriptSnapshot`. |
|
||||||
|
| `TextDocuments<T>` | Manages a collection of documents, emits `onDidOpen`, `onDidChangeContent`, `onDidClose`, `onDidSave` events. |
|
||||||
|
| `createUriMap()` | Helper for URI-based maps used by documents/projects. |
|
||||||
|
|
||||||
|
**Usage Tips:**
|
||||||
|
- Always call `documents.listen()` with your editor’s change events so the language service receives updates.
|
||||||
|
- Dispose documents when closed to release snapshots and avoid memory leaks.
|
||||||
|
- Use `editor.connection.getWorkspaceEdit()` to apply edits triggered by code actions/rename.
|
||||||
|
|
||||||
|
## `@volar/language-service`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createLanguageService(language, plugins, env, project)` | Core factory returning a `LanguageService` with `context`, `languageFeatures`, and `commands`. |
|
||||||
|
| `LanguageServicePlugin` | Interface for authoring plugins (see [Plugin Authoring](plugin-authoring.md)). |
|
||||||
|
| `createUriMap`, `mergeWorkspaceEdits`, `transformWorkspaceEdit` | Utility helpers used when composing plugins or handling edits. |
|
||||||
|
| `FileSystem`, `LanguageServiceEnvironment` | Shapes describing filesystem + configuration hooks for the service. |
|
||||||
|
|
||||||
|
**Capabilities:** `languageService.languageFeatures` exposes methods corresponding to LSP requests: `getDiagnostics`, `getCompletionItems`, `getHover`, `getDefinition`, `getCodeActions`, etc.
|
||||||
|
|
||||||
|
## `@volar/language-core`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createLanguage(plugins, scriptRegistry, sync)` | Builds a `Language` instance that tracks virtual files/embedded codes emitted by language plugins. |
|
||||||
|
| `forEachEmbeddedCode` | Generator that iterates embedded files recursively. |
|
||||||
|
| `LanguagePlugin` | Interface for describing how to turn source files into virtual code (see [Source Map & Code Gen](source-map-and-code-gen.md)). |
|
||||||
|
| `defaultMapperFactory`, `LinkedCodeMap` | Tools for building and traversing source maps. |
|
||||||
|
|
||||||
|
**Snapshots:** Use `language.scripts.get(id)` to fetch scripts and `language.scripts.set(id, snapshot, languageId)` to register new ones.
|
||||||
|
|
||||||
|
## `@volar/typescript`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createLanguageServiceHost(ts, sys, language, asScriptId, projectHost)` | Bridges Volar’s `Language` with TypeScript’s `LanguageServiceHost`. |
|
||||||
|
| `decorateLanguageServiceHost(host, opts)` | Augments an existing TS host with Volar hooks. |
|
||||||
|
| `createUriConverter(rootFolders)` | Shared helper for converting between file paths and `vscode-uri` instances. |
|
||||||
|
| `createSys` | Builds a virtual `ts.System` that respects Volar’s FS abstraction. |
|
||||||
|
|
||||||
|
**TypeScript Plugin Hooks:** Within a language plugin you can expose `typescript.extraFileExtensions`, `types` definitions, and `getServiceScript` to feed TS the generated files.
|
||||||
|
|
||||||
|
## `@volar/language-server`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createServer(connection)` | Low-level factory returning a server object (documents, workspaceFolders, fileWatcher, languageFeatures, etc.). |
|
||||||
|
| `createConnection()` | Convenience for creating a VS Code-style connection with proposed features enabled. |
|
||||||
|
| `createSimpleProject(languagePlugins)` | Minimal project implementation for non-TypeScript scenarios. |
|
||||||
|
| `createTypeScriptProject(ts, tsLocalized, create)` | Full-featured project that manages TS configs, watchers, and workspace diagnostics. |
|
||||||
|
|
||||||
|
**Features:** `server.languageFeatures.requestRefresh(clearDiagnostics)` forces clients to refresh diagnostics (useful after config changes).
|
||||||
|
|
||||||
|
## `@volar/kit`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createLanguageServer(options)` | Batteries-included bootstrapper that wires connection, projects, configuration reloads, and restart commands. |
|
||||||
|
| `createProjectFactory(opts)` | Helper for building project loaders compatible with the kit. |
|
||||||
|
|
||||||
|
Use `@volar/kit` when you want a ready-made server; drop down to `@volar/language-server` for custom wiring.
|
||||||
|
|
||||||
|
## `@volar/monaco`
|
||||||
|
|
||||||
|
See [Monaco Playground](monaco-playground.md).
|
||||||
|
|
||||||
|
### Key Helpers
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `setupLanguageServiceForMonaco(monaco, loaders)` | Registers language service worker + TypeScript loader. |
|
||||||
|
| `languageServiceHost` (returned) | Provides `writeFile`, `syncFile`, `renameFile` helpers for your VFS. |
|
||||||
|
|
||||||
|
`loadTypescript` and `loadLanguageService` should return promises so the worker bundles lazily.
|
||||||
|
|
||||||
|
## `@volar/cli` (if used)
|
||||||
|
|
||||||
|
While not covered elsewhere, some community tools use `@volar/cli` as a thin wrapper around the language service. When writing your own CLI, follow the patterns in [CLI Integration](cli-integration.md).
|
||||||
|
## `@volar/vue-language-core`
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createVueLanguagePlugin(ts, compilerOptions, vueCompilerOptions, asFileName)` | Default language plugin for `.vue` SFCs. |
|
||||||
|
| `getAllExtensions(vueCompilerOptions)` | Returns file extensions treated as Vue components. |
|
||||||
|
| Utilities (`parseSfc`, `compileTemplate`, etc.) | Used when customizing SFC parsing/compilation. |
|
||||||
|
|
||||||
|
Customize via `vueCompilerOptions` (macros, experimental flags). See [TypeScript Plugin Deep Dive](typescript-plugin-deep-dive.md).
|
||||||
|
|
||||||
|
## `@volar/vue-language-service`
|
||||||
|
|
||||||
|
Wraps `@volar/language-service` with Vue-specific plugins (template completions, directives, twoslash queries).
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `getFullLanguageServicePlugins(ts, opts)` | Returns the default plugin array (TS semantics, CSS, Emmet, Vue-specific features). |
|
||||||
|
| `getHybridModeLanguageServicePlugins(ts, getTsPluginClient)` | Reduced set for hybrid environments (e.g., Take Over Mode). |
|
||||||
|
| `getVueLanguagePlugin(ts, compilerOptions, vueCompilerOptions)` | Convenience wrapper to create the language plugin. |
|
||||||
|
|
||||||
|
Use these helpers when you want the stock Vue feature set without reconfiguring every service manually.
|
||||||
|
|
||||||
|
## `@volar/vue-typescript`
|
||||||
|
|
||||||
|
TypeScript plugin that injects `.vue` support into `tsserver`.
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `createLanguageServerPlugin(ts, resolveConfig)` | Entry point for wiring the plugin inside `@volar/language-server` / `@volar/kit`. |
|
||||||
|
| `createTypeScriptPlugin(ts, config)` | Low-level API for registering with `tsserver` directly. |
|
||||||
|
| `resolveConfig(workspaceUri)` | Hook for returning `vueCompilerOptions` per workspace. |
|
||||||
|
|
||||||
|
See [TypeScript Plugin Deep Dive](typescript-plugin-deep-dive.md) for details.
|
||||||
|
|
||||||
|
## `@vue/language-core`
|
||||||
|
|
||||||
|
Vue compiler utilities used under the hood by Volar. Key exports include:
|
||||||
|
|
||||||
|
- `createTsLanguageServiceHost(ts, options)` – used when invoking Vue’s TS integrations directly.
|
||||||
|
- `compileScript`, `compileTemplate` – SFC compilation helpers (useful when customizing macros or template transforms).
|
||||||
|
- `parseSFC` – parse `.vue` files into descriptor objects.
|
||||||
|
|
||||||
|
While Volar wraps most of these, advanced integrations may call them directly when customizing compile steps.
|
||||||
|
|
||||||
|
With this reference, you can quickly locate the right factory or helper for any integration scenario, complementing the narrative guides throughout the docs.
|
||||||
169
docs/performance-and-debugging.md
Normal file
169
docs/performance-and-debugging.md
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# Performance & Debugging Playbook
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Testing & CI](testing-and-ci.md) • [Telemetry](telemetry-and-observability.md)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
1. **Measure first** – Always gather profiles/metrics before optimizing.
|
||||||
|
2. **Avoid duplication** – Hook into Volar’s existing document, project, and TypeScript caches instead of re-reading files.
|
||||||
|
3. **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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --inspect-brk dist/server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use Chrome DevTools or VS Code’s debugger to capture CPU profiles and heap snapshots.
|
||||||
|
- Focus on `provideDiagnostics` and `provideCompletionItems` if latency spikes while typing.
|
||||||
|
|
||||||
|
### 3. TypeScript Tracing
|
||||||
|
|
||||||
|
Enable TS compiler traces to see how TypeScript handles your virtual files:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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:
|
||||||
|
|
||||||
|
1. Verify `provideDiagnostics` runs by logging entry/exit.
|
||||||
|
2. Ensure mappings have `verification: true`.
|
||||||
|
3. Confirm the document’s language ID matches your plugin’s target.
|
||||||
|
4. Check `connection.sendDiagnostics` for errors (watch the LSP trace).
|
||||||
|
|
||||||
|
### Scenario 2: Incorrect Hover Text
|
||||||
|
|
||||||
|
1. Inspect the generated virtual code (`codeGen.getText()`) to ensure the content is what you expect.
|
||||||
|
2. Use `@volar/source-map` to verify the mapping covers the range you hovered.
|
||||||
|
3. Check that `provideHover` returns `MarkdownString`/`string` in the format the client expects (no stray triple backticks unless intended).
|
||||||
|
|
||||||
|
### Scenario 3: Memory Leak
|
||||||
|
|
||||||
|
1. Take heap snapshots before/after opening large files.
|
||||||
|
2. Confirm you dispose snapshots (call `dispose` on `IScriptSnapshot` if you allocate custom ones).
|
||||||
|
3. Remove references to old projects/documents when workspace folders change (watch `server.workspaceFolders.onDidChange`).
|
||||||
|
|
||||||
|
### Scenario 4: Take Over Mode Conflicts
|
||||||
|
|
||||||
|
1. Verify only one TypeScript server is running (disable built-in TS in VS Code when using Volar).
|
||||||
|
2. Check `tsserver.log` to ensure the Volar TS plugin is loaded once per project.
|
||||||
|
3. Provide documentation/instructions for users to disable conflicting extensions.
|
||||||
|
|
||||||
|
## Logging & Diagnostics
|
||||||
|
|
||||||
|
### Structured Logging
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const log = (message: string, data?: unknown) => {
|
||||||
|
connection.console.info(`[json-yaml] ${message} ${data ? JSON.stringify(data) : ''}`);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use `connection.console` for logs visible in the client’s output channel.
|
||||||
|
- Reserve `console.error` for actionable failures (schema fetch errors, config parse failures).
|
||||||
|
|
||||||
|
### Telemetry Hooks
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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 `resolveCompletionItem` only 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 `createTypeScriptProject` once 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
|
||||||
|
|
||||||
|
1. **Is the issue limited to specific files?** Inspect their generated output and mappings.
|
||||||
|
2. **Does it happen after hot reload or configuration changes?** Check `applyConfiguration` to ensure you’re not re-registering plugins or leaking state.
|
||||||
|
3. **Is TypeScript raising errors?** Examine `tsserver.log` and ensure your TS plugin is registering correctly.
|
||||||
|
4. **Are you duplicating watchers?** One file watcher per project is sufficient; extra watchers can saturate the event loop.
|
||||||
|
5. **Is schema resolution slow?** Provide `resolveWorkspaceUri` to 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.
|
||||||
281
docs/plugin-authoring.md
Normal file
281
docs/plugin-authoring.md
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
# Authoring Volar Plugins & Language Services
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Code Gen Guide](source-map-and-code-gen.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Volar’s architecture is deliberately modular: language plugins transform source files into virtual code, while language-service plugins add editor-facing features such as completions, hovers, diagnostics, and code actions. This guide documents every hook, option, flag, and best practice so you can author custom functionality with confidence.
|
||||||
|
|
||||||
|
## Mental Model
|
||||||
|
|
||||||
|
```
|
||||||
|
Source file ──▶ Language plugin (virtual code + source maps) ──▶ Language service plugin (capabilities) ──▶ Host (LSP, Monaco, CLI)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Language plugins** live in `@volar/language-core`. They describe how a file decomposes into embedded scripts (e.g., templates, styles) and how generated code maps back to the original source.
|
||||||
|
- **Language service plugins** live in `@volar/language-service`. They consume the virtual code graph and implement hover/completion/diagnostics, etc.
|
||||||
|
- **TypeScript plugins** (part of `@volar/typescript`) bridge Volar’s virtual code into the TS language service so TypeScript-based tooling understands your files.
|
||||||
|
|
||||||
|
## Language Plugin API (`@volar/language-core`)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { LanguagePlugin, VirtualCode, CodegenContext, IScriptSnapshot } from '@volar/language-core';
|
||||||
|
|
||||||
|
const myLanguagePlugin: LanguagePlugin = {
|
||||||
|
getLanguageId(scriptId) {
|
||||||
|
if (scriptId.endsWith('.foo')) return 'foo';
|
||||||
|
},
|
||||||
|
createVirtualCode(scriptId, languageId, snapshot, ctx) {
|
||||||
|
const code = transformFooToTs(snapshot.getText(0, snapshot.getLength()));
|
||||||
|
return {
|
||||||
|
id: scriptId + '.ts',
|
||||||
|
languageId: 'typescript',
|
||||||
|
snapshot: createSnapshot(code),
|
||||||
|
mappings: buildMappings(snapshot, code),
|
||||||
|
embeddedCodes: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateVirtualCode(scriptId, previous, newSnapshot, ctx) {
|
||||||
|
// Optional incremental update. Return undefined to fallback to createVirtualCode.
|
||||||
|
},
|
||||||
|
disposeVirtualCode(scriptId, virtualCode) {
|
||||||
|
virtualCode.snapshot.dispose?.();
|
||||||
|
},
|
||||||
|
isAssociatedFileOnly(scriptId) {
|
||||||
|
// Return true if the file exists purely to feed generated code (rare).
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Fields
|
||||||
|
|
||||||
|
- `id`: unique identifier for the virtual code (commonly `${sourceId}.${ext}`).
|
||||||
|
- `languageId`: logical language (e.g., `typescript`, `html`, `json`). Hosts use this to decide which plugins run.
|
||||||
|
- `snapshot`: an `IScriptSnapshot` describing the generated text. Use `ts.ScriptSnapshot.fromString` or a custom snapshot implementation.
|
||||||
|
- `mappings`: array of `CodeMapping` entries describing how generated ranges map back to source ranges.
|
||||||
|
- `embeddedCodes`: nested virtual codes (e.g., template -> script -> CSS). Walk them recursively so downstream plugins see the full graph.
|
||||||
|
- `linkedCodeMappings`: optional cross-file relationships (e.g., macro expansions). Populate when features like rename need multi-file context.
|
||||||
|
|
||||||
|
### Mapping Flags (`CodeInformation`)
|
||||||
|
|
||||||
|
Each mapping entry can enable/disable specific language features:
|
||||||
|
|
||||||
|
- `verification`: allow diagnostics/code actions. Use `false` for generated scaffolding you don’t want to expose.
|
||||||
|
- `completion`: mark sections as completion targets. Use `onlyImport` for import-only completions.
|
||||||
|
- `semantic`: enable hover/inlay highlight generation.
|
||||||
|
- `navigation`: rename/reference/definition support.
|
||||||
|
- `structure`: drive folding/outline.
|
||||||
|
- `format`: allow formatting engines to reflow the generated region.
|
||||||
|
|
||||||
|
**Best practice:** map only the user-authored slices of generated code. If a block of your generated TS is purely synthetic, set `verification: false` and omit `navigation` so diagnostics and rename never escape into that region.
|
||||||
|
|
||||||
|
## TypeScript Bridge (`@volar/typescript`)
|
||||||
|
|
||||||
|
If you need TypeScript to understand your virtual files:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createLanguageServiceHost } from '@volar/typescript';
|
||||||
|
|
||||||
|
const { languageServiceHost, getExtraServiceScript } = createLanguageServiceHost(
|
||||||
|
ts,
|
||||||
|
sys,
|
||||||
|
language,
|
||||||
|
(fileName) => fileName as URI,
|
||||||
|
projectHost,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Extra service scripts**: use `languagePlugin.typescript?.getServiceScript` to expose additional virtual files (e.g., template-extracted scripts) directly to the TS service.
|
||||||
|
- **Incremental programs**: `createTypeScriptProject` (from `@volar/language-server/lib/project/typescriptProject`) wraps the TS host with file watching and multi-config support.
|
||||||
|
|
||||||
|
### TypeScript Plugin Options
|
||||||
|
|
||||||
|
Within a language plugin you can define `typescript` options:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const plugin: LanguagePlugin = {
|
||||||
|
typescript: {
|
||||||
|
extraFileExtensions: [{ extension: 'foo', isMixedContent: false, scriptKind: ts.ScriptKind.Deferred }],
|
||||||
|
getServiceScript(root) { /* return TypeScript service script */ },
|
||||||
|
getExtraServiceScripts(fileName, root) { /* return additional scripts (d.ts, .ts) */ },
|
||||||
|
resolveHiddenExtensions: true, // allow hidden files to be used by TS
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use `extraFileExtensions` so TypeScript doesn’t treat `.foo` as plain text.
|
||||||
|
- `resolveHiddenExtensions`: set to `true` if your virtual files live under hidden paths (e.g., `.volar` dirs) and still need TS features.
|
||||||
|
|
||||||
|
## Language Service Plugin API (`@volar/language-service`)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { LanguageServicePlugin } from '@volar/language-service';
|
||||||
|
|
||||||
|
const myServicePlugin: LanguageServicePlugin = {
|
||||||
|
name: 'foo-diagnostics',
|
||||||
|
capabilities: {
|
||||||
|
diagnosticProvider: true,
|
||||||
|
completionProvider: { triggerCharacters: [':'] },
|
||||||
|
codeActionProvider: true,
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
return {
|
||||||
|
provideDiagnostics(document) {
|
||||||
|
return [{ message: 'Invalid foo', range: ... }];
|
||||||
|
},
|
||||||
|
provideCompletionItems(document, position) {
|
||||||
|
return { items: [{ label: 'fooOption' }] };
|
||||||
|
},
|
||||||
|
provideCodeActions(document, range, context) {
|
||||||
|
return [{ title: 'Fix foo', edit: ... }];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Hooks
|
||||||
|
|
||||||
|
- `provideDiagnostics(document)` – return an array of `Diagnostic`.
|
||||||
|
- `provideCompletionItems(document, position, context)` – return `CompletionList`.
|
||||||
|
- `resolveCompletionItem(item)` – add lazy detail (e.g., docs).
|
||||||
|
- `provideHover(document, position)` – return `Hover`.
|
||||||
|
- `provideDocumentSymbols`, `provideFoldingRanges`, `provideSelectionRanges`.
|
||||||
|
- `provideDefinition`, `provideTypeDefinition`, `provideImplementation`.
|
||||||
|
- `provideCodeActions`, `resolveCodeAction`.
|
||||||
|
- `provideCodeLens`, `resolveCodeLens`.
|
||||||
|
- `provideDocumentFormattingEdits` / `provideDocumentOnTypeFormattingEdits`.
|
||||||
|
- `provideLinkedEditingRanges`, `provideDocumentHighlights`.
|
||||||
|
- `provideDocumentLinks`, `resolveDocumentLink`.
|
||||||
|
- `provideInlayHints`, `resolveInlayHint`.
|
||||||
|
- `provideMoniker`, `provideInlineValues`.
|
||||||
|
- `provideSemanticTokens`, `provideWorkspaceSymbols`.
|
||||||
|
- `provideDocumentDropEdits`, `provideAutoInsert`, `provideCommand`.
|
||||||
|
|
||||||
|
**Capabilities Flag Reference:**
|
||||||
|
|
||||||
|
Set the matching flag in `capabilities` to signal what hooks you implement:
|
||||||
|
|
||||||
|
- `diagnosticProvider: true`
|
||||||
|
- `completionProvider: { triggerCharacters, resolveProvider }`
|
||||||
|
- `hoverProvider: true`
|
||||||
|
- `codeActionProvider: true`
|
||||||
|
- `documentFormattingProvider`, `documentOnTypeFormattingProvider`
|
||||||
|
- `documentSymbolProvider`, `workspaceSymbolProvider`
|
||||||
|
- `documentLinkProvider`
|
||||||
|
- `codeLensProvider`
|
||||||
|
- `inlayHintProvider`
|
||||||
|
- `semanticTokensProvider`
|
||||||
|
- `autoInsertionProvider`
|
||||||
|
- `documentDropProvider`
|
||||||
|
- `monikerProvider`
|
||||||
|
- `inlineValueProvider`
|
||||||
|
|
||||||
|
**Best practice:** Always set the minimal capability set, so hosts skip calling hooks you don’t implement.
|
||||||
|
|
||||||
|
## Registering Plugins
|
||||||
|
|
||||||
|
### Language-Core Plugins
|
||||||
|
|
||||||
|
When creating a language core:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createLanguage, createUriMap } from '@volar/language-core';
|
||||||
|
|
||||||
|
const language = createLanguage(
|
||||||
|
[myLanguagePlugin],
|
||||||
|
createUriMap(false),
|
||||||
|
(uri, includeFs, shouldRegister) => { /* sync hook */ },
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Language-Service Plugins
|
||||||
|
|
||||||
|
When creating a language service:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createLanguageService } from '@volar/language-service';
|
||||||
|
|
||||||
|
const service = createLanguageService(language, [myServicePlugin], env, project);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Language Server Integration
|
||||||
|
|
||||||
|
In `@volar/language-server`, register plugins via the project factory:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
createTypeScriptProject(ts, tsLocalized, async (context) => ({
|
||||||
|
languagePlugins: [myLanguagePlugin],
|
||||||
|
setup({ language, project }) {
|
||||||
|
project.languageServicePlugins.push(myServicePlugin);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
For non-TS projects, pass the plugins directly to `createSimpleProject`.
|
||||||
|
|
||||||
|
## End-to-End Example: Custom `.foo` Language
|
||||||
|
|
||||||
|
1. **Language plugin**: parse `.foo` files, translate them into `.ts` virtual code with source maps.
|
||||||
|
2. **Language service plugin**: inspect the `.foo` AST (via metadata stored in the snapshot) to provide diagnostics and completions.
|
||||||
|
3. **TypeScript integration**: register `.foo` via `extraFileExtensions` so TS sees the generated `.ts` output.
|
||||||
|
4. **Server**: include the plugins in your Volar language server project and expose configuration toggles via `server.configurations`.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// fooLanguagePlugin.ts
|
||||||
|
export const fooLanguagePlugin: LanguagePlugin = {
|
||||||
|
getLanguageId(scriptId) {
|
||||||
|
if (scriptId.endsWith('.foo')) return 'foo';
|
||||||
|
},
|
||||||
|
createVirtualCode(scriptId, languageId, snapshot) {
|
||||||
|
const { code, mappings } = compileFoo(snapshot.getText(0, snapshot.getLength()));
|
||||||
|
return {
|
||||||
|
id: scriptId + '.ts',
|
||||||
|
languageId: 'typescript',
|
||||||
|
snapshot: ts.ScriptSnapshot.fromString(code),
|
||||||
|
mappings,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
typescript: {
|
||||||
|
extraFileExtensions: [{ extension: 'foo', isMixedContent: false, scriptKind: ts.ScriptKind.Deferred }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// fooServicePlugin.ts
|
||||||
|
export const fooServicePlugin: LanguageServicePlugin = {
|
||||||
|
name: 'foo',
|
||||||
|
capabilities: {
|
||||||
|
diagnosticProvider: true,
|
||||||
|
completionProvider: { triggerCharacters: [':'] },
|
||||||
|
hoverProvider: true,
|
||||||
|
},
|
||||||
|
create({ language }) {
|
||||||
|
return {
|
||||||
|
provideDiagnostics(document) { /* ... */ },
|
||||||
|
provideCompletionItems(document, position) { /* ... */ },
|
||||||
|
provideHover(document, position) { /* ... */ },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server setup
|
||||||
|
const project = createSimpleProject([fooLanguagePlugin]);
|
||||||
|
const languageServicePlugins = [fooServicePlugin];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices Checklist
|
||||||
|
|
||||||
|
1. **Type-safe snapshots** – store metadata (ASTs, parse errors) on the snapshot instance to avoid reparsing in service plugins.
|
||||||
|
2. **Map-only relevant regions** – keep mappings minimal to reduce LSP payload sizes and avoid duplicate diagnostics on synthetic sections.
|
||||||
|
3. **Handle incremental updates** – implement `updateVirtualCode` for large files so you don’t recreate snapshots on every keystroke.
|
||||||
|
4. **Watch configuration** – expose settings via `server.configurations` and apply them inside your plugins (e.g., lint severity).
|
||||||
|
5. **Test every hook** – use the `vscode-languageserver` test harness to simulate completions, hovers, diagnostics, etc., and assert on the results.
|
||||||
|
6. **Profile plugin performance** – wrap expensive hooks (diagnostics, completions) with timing logs in development to catch regressions early.
|
||||||
|
7. **Respect cancellation** – if your plugin performs async work (e.g., fetch data), cancel promptly when the provided token fires.
|
||||||
|
8. **Document capabilities** – consumers should know what your plugin does (e.g., “Provides hover + quick fixes for .foo files but not formatting”).
|
||||||
|
9. **Wire telemetry** – emit `connection.telemetry.logEvent` for significant plugin actions (cache misses, external request failures) to aid debugging.
|
||||||
|
10. **Stay in sync with Volar releases** – watch for changes in `@volar/language-*` packages; run integration tests against new versions to catch API shifts.
|
||||||
|
|
||||||
|
By following these steps and filling in all hooks, flags, and options, your Volar plugins will integrate cleanly with any host—from VS Code to custom CLI tooling—while delivering full-fidelity language intelligence.
|
||||||
62
docs/release-and-upgrade.md
Normal file
62
docs/release-and-upgrade.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Upgrading VolarJS: Release & Compatibility Guide
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Testing & CI](testing-and-ci.md) • [Performance Guide](performance-and-debugging.md)
|
||||||
|
|
||||||
|
This guide outlines a safe upgrade path when bumping Volar, TypeScript, or Node versions across your tooling.
|
||||||
|
|
||||||
|
## Version Matrix
|
||||||
|
|
||||||
|
| Volar Major | Recommended TS | Minimum Node | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1.x | 4.9–5.0 | Node 14+ | Legacy support for Vue 2 + early Vue 3. |
|
||||||
|
| 2.x | 5.0–5.4 | Node 16+ | Current stable; aligns with Vue 3.4+. |
|
||||||
|
| 3.x (alpha) | 5.4+ | Node 18+ | Experimental features (volar-next). |
|
||||||
|
|
||||||
|
Always align `@volar/*`, `@vue/language-*`, and `typescript` versions to the matrix above.
|
||||||
|
|
||||||
|
## Upgrade Checklist
|
||||||
|
|
||||||
|
1. **Pin Dependencies** – update `package.json` to the target versions; use exact versions for deterministic builds.
|
||||||
|
2. **Update Tooling** – ensure VS Code extension, CLI, and server dependencies all reference the same Volar release.
|
||||||
|
3. **Run Tests** – execute unit/integration tests (see [Testing & CI](testing-and-ci.md)).
|
||||||
|
4. **Benchmark** – re-run performance benchmarks (see [LSP Benchmarking](lsp-benchmarking.md)).
|
||||||
|
5. **Smoke Test Editors** – open representative projects in VS Code/Neovim/etc.
|
||||||
|
6. **Document Changes** – highlight breaking changes or new config fields in your release notes.
|
||||||
|
|
||||||
|
## Handling Breaking Changes
|
||||||
|
|
||||||
|
- **Config**: When introducing new settings (e.g., `volarJsonYaml.schemas`), provide defaults and migration instructions.
|
||||||
|
- **APIs**: If your custom plugins rely on Volar internals, watch release notes for API removals/renames and adapt accordingly.
|
||||||
|
- **TypeScript**: Major TS upgrades may require updating `tsconfig` (e.g., new `lib` entries) and re-running `tsc --build`.
|
||||||
|
|
||||||
|
## Testing Multiple Versions
|
||||||
|
|
||||||
|
Use `npm` aliases or `pnpm overrides` to test against multiple Volar/TS combos:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install typescript@5.0 @volar/language-service@2.2.1
|
||||||
|
npm test
|
||||||
|
npm install typescript@5.4 @volar/language-service@2.4.0
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Automate via CI matrix:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
ts: ["5.0", "5.4"]
|
||||||
|
volar: ["2.2.1", "2.4.0"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
- Monitor [release notes](https://github.com/vuejs/language-tools/releases) for deprecations (e.g., old config flags, plugin APIs).
|
||||||
|
- When a feature is deprecated, surface warnings in your logs and plan migrations ahead of the removal.
|
||||||
|
|
||||||
|
## Communication
|
||||||
|
|
||||||
|
- Publish a migration guide whenever you bump major versions—include new settings, removal of flags, and compatibility notes.
|
||||||
|
- For consumers of your tooling, provide sample upgrade scripts (e.g., `npx volar-migrate`).
|
||||||
|
|
||||||
|
Staying proactive with upgrades keeps your tooling aligned with Vue’s evolution and prevents users from encountering surprise regressions.
|
||||||
71
docs/response-formatting.md
Normal file
71
docs/response-formatting.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Response Formatting & Content Best Practices
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [UX Best Practices](ux-best-practices.md) • [Telemetry & Observability](telemetry-and-observability.md)
|
||||||
|
|
||||||
|
Great tooling depends not only on fast responses but also on clear, consistent payloads. This guide outlines best practices for shaping diagnostics, hover content, completion items, and code actions so users get precise information without noise.
|
||||||
|
|
||||||
|
## Diagnostics
|
||||||
|
|
||||||
|
### Content
|
||||||
|
|
||||||
|
- **Source**: Set `diagnostic.source = 'rule-name'` (e.g., `json-schema`, `custom-lint`). Helps users filter.
|
||||||
|
- **Code**: Provide a stable identifier (`diagnostic.code = 'missing-prop'`), enabling ignore lists or quick fixes.
|
||||||
|
- **Message**: Include cause + remediation (“Prop `foo` missing. Declare it in `<script setup> defineProps`.”).
|
||||||
|
- **Severity**: Follow LSP conventions (1 = Error, 2 = Warning, 3 = Information, 4 = Hint). Allow configuration to downgrade.
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- Batch diagnostics per document. Avoid sending duplicates for nested ranges.
|
||||||
|
- When possible, include `relatedInformation` for cross-file context but cap the number to avoid huge payloads.
|
||||||
|
|
||||||
|
## Hover Content
|
||||||
|
|
||||||
|
- Prefer Markdown; wrap code snippets in fenced blocks (```ts … ```).
|
||||||
|
- Trim trailing whitespace and collapse excessive blank lines.
|
||||||
|
- Provide primary info first (type/value) and link to docs for detail.
|
||||||
|
- Strip sensitive data (absolute paths, user tokens).
|
||||||
|
|
||||||
|
### Context Tips
|
||||||
|
|
||||||
|
- For schema-derived hovers, include the schema title and a short description; keep JSON schema dumps behind a collapsible section (use `<details>` if the client supports it).
|
||||||
|
|
||||||
|
## Completion Items
|
||||||
|
|
||||||
|
- **Label**: concise name (“`v-if`”, “`<MyComponent>`”).
|
||||||
|
- **Detail**: extra context (prop types, component path).
|
||||||
|
- **Documentation**: Markdown doc snippet.
|
||||||
|
- **SortText**: use prefixes (e.g., `a_`, `b_`) to prioritize built-ins vs user symbols.
|
||||||
|
- **InsertText**: prefer snippets for props/attributes; escape `$`/`}`.
|
||||||
|
- **Additional Text Edits**: use sparingly; ensure they do not conflict with user edits.
|
||||||
|
|
||||||
|
## Code Actions & Quick Fixes
|
||||||
|
|
||||||
|
- Provide descriptive titles (“Add `$schema` reference”).
|
||||||
|
- Group actions by kind (`quickfix`, `source.organizeImports`).
|
||||||
|
- Include diagnostics references so editors can tie actions to specific issues.
|
||||||
|
- Bundle workspace edits that can be applied atomically; avoid overlapping edits from multiple actions.
|
||||||
|
|
||||||
|
## Formatting Edits
|
||||||
|
|
||||||
|
- Respect editor options (`tabSize`, `insertSpaces`).
|
||||||
|
- Ensure edits are deterministic; format the entire document only when necessary.
|
||||||
|
- For large files, prefer range formatting to reduce payload size.
|
||||||
|
|
||||||
|
## Response Size & Throttling
|
||||||
|
|
||||||
|
- Cap the number of completions/diagnostics per request (e.g., 500) and log when truncation occurs.
|
||||||
|
- Debounce responses triggered by rapid changes (especially diagnostics and code lenses).
|
||||||
|
- Use `ResultProgressReporter` (where supported) for long-running operations to stream partial results.
|
||||||
|
|
||||||
|
## Consistency Across Contexts
|
||||||
|
|
||||||
|
- Reuse formatting helpers in LSP, CLI, and Monaco contexts so users see identical messages.
|
||||||
|
- When building a CLI, mirror VS Code’s diagnostic format (file:line:char severity message) for familiarity.
|
||||||
|
|
||||||
|
## Testing & Validation
|
||||||
|
|
||||||
|
- Snapshot hover/completion payloads to detect accidental format regressions.
|
||||||
|
- Use integration tests to ensure markdown renders properly in target editors (VS Code’s `MarkdownString` preview, etc.).
|
||||||
|
- Validate JSON output (CLI) against a schema so downstream tooling can rely on field names/types.
|
||||||
|
|
||||||
|
By focusing on content quality, context, and payload size, you deliver responses that are not only fast but also clear and actionable, regardless of editor or CLI. EOF
|
||||||
202
docs/source-map-and-code-gen.md
Normal file
202
docs/source-map-and-code-gen.md
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Deep Dive: `@volar/source-map` & `@volar/code-gen`
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Performance Guide](performance-and-debugging.md)
|
||||||
|
|
||||||
|
When you author language plugins, you must translate source text into generated code without losing the ability to map diagnostics, hovers, and refactors back to the original file. Volar provides two foundational packages for this job:
|
||||||
|
|
||||||
|
- `@volar/source-map`: low-level mapping utilities used everywhere Volar needs to relate two coordinate spaces (source ↔ generated).
|
||||||
|
- `@volar/code-gen`: higher-level builder that emits text and source maps in lockstep, making it painless to describe complex virtual files.
|
||||||
|
|
||||||
|
This guide explains every exported API, flag, and workflow, with exhaustive examples and best practices.
|
||||||
|
|
||||||
|
## `@volar/source-map` Basics
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { SourceMap, Mapping, CodeInformation } from '@volar/source-map';
|
||||||
|
|
||||||
|
const map = new SourceMap<CodeInformation>();
|
||||||
|
map.add({
|
||||||
|
data: { verification: true, navigation: true },
|
||||||
|
sourceRange: [srcStart, srcEnd],
|
||||||
|
mappedRange: [genStart, genEnd],
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapped = map.getMappedRange([srcStart, srcEnd]);
|
||||||
|
const reverse = map.getSourceRange([genStart, genEnd]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mapping Fields
|
||||||
|
|
||||||
|
- `sourceRange`: `[startOffset, endOffset]` in the original document.
|
||||||
|
- `mappedRange`: `[startOffset, endOffset]` in the generated document.
|
||||||
|
- `data`: a `CodeInformation` object controlling feature visibility:
|
||||||
|
- `verification`: allow diagnostics + code actions.
|
||||||
|
- `completion`: allow completions; ` { isAdditional: true }` to mark extra suggestions (imports, etc.).
|
||||||
|
- `semantic`: enable hover/inlay semantics.
|
||||||
|
- `navigation`: rename/reference/definition support.
|
||||||
|
- `structure`: outlines/folding.
|
||||||
|
- `format`: formatting support.
|
||||||
|
|
||||||
|
Use the `SourceMap` methods:
|
||||||
|
|
||||||
|
- `getMappedRange(range, filter?)` – yields generated segments for a source range.
|
||||||
|
- `getSourceRange(range, filter?)` – inverse lookup.
|
||||||
|
- `forEach()` – iterate all mappings.
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **Minimal coverage** – map only the user-authored slices; synthetic scaffolding should either be unmapped or flagged with `verification: false`.
|
||||||
|
2. **Feature-specific filters** – when computing rename ranges, pass a filter to `getMappedRange` that checks `data.navigation`.
|
||||||
|
3. **Chunk by content type** – create separate mappings for template vs script vs style segments so downstream plugins can enable/disable features per chunk.
|
||||||
|
|
||||||
|
## `@volar/code-gen` Workflow
|
||||||
|
|
||||||
|
`@volar/code-gen` builds on the source-map APIs and gives you a friendly builder for emitting text + mappings simultaneously.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { CodeGen } from '@volar/code-gen';
|
||||||
|
|
||||||
|
const codeGen = new CodeGen();
|
||||||
|
codeGen.addText('const components = ');
|
||||||
|
codeGen.addCode(
|
||||||
|
'getComponents()',
|
||||||
|
{ start: templateOffset, end: templateOffset + 13 },
|
||||||
|
{
|
||||||
|
verification: true,
|
||||||
|
navigation: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
codeGen.addText(';\n');
|
||||||
|
|
||||||
|
const text = codeGen.getText();
|
||||||
|
const mappings = codeGen.getMappings();
|
||||||
|
const embeddedFiles = codeGen.getEmbeddedFiles();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Methods
|
||||||
|
|
||||||
|
- `addText(text: string)` – append raw generated text (no mapping).
|
||||||
|
- `addCode(generatedText, sourceRange, codeInfo)` – append text and register a mapping back to the source.
|
||||||
|
- `addMapping(mapping)` – manually push a `Mapping`.
|
||||||
|
- `addMapping2` (advanced) – register multi-segment mappings.
|
||||||
|
- `addExtraMapping` – for completion-only or formatting-only segments.
|
||||||
|
- `getText()` – returns the generated string.
|
||||||
|
- `getMappings()` – returns an array of `Mapping<CodeInformation>`.
|
||||||
|
- `getLength()` – current length of generated text.
|
||||||
|
- `getEmbeddedFiles()` – nested `CodeGen` instances for embedded content (templates within scripts, etc.).
|
||||||
|
- `getTextDocument(uri, languageId)` – convenience for building a `TextDocument` from the generated content.
|
||||||
|
|
||||||
|
### Embedded Files
|
||||||
|
|
||||||
|
Use `codeGen.addEmbeddedFile(file)` when your language plugin produces nested virtual documents (e.g., a `<template>` section that becomes HTML + TS). Each embedded file is itself a `CodeGen` instance with its own text/mappings.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const templateFile = codeGen.addEmbeddedFile('template.html');
|
||||||
|
templateFile.addText('<div>');
|
||||||
|
templateFile.addCode('{{ value }}', sourceRange, codeInfo);
|
||||||
|
templateFile.addText('</div>');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flags & Options
|
||||||
|
|
||||||
|
`CodeGen` constructor accepts options:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const codeGen = new CodeGen({
|
||||||
|
useBlockNewLine: true, // automatically insert newline when switching blocks
|
||||||
|
mappingMode: 'offset' | 'lengthDiff', // default 'offset'
|
||||||
|
mapRange: true, // include mapped ranges even for addText (rare)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- `useBlockNewLine`: helpful when generating multi-line scripts; ensures mappings stay aligned.
|
||||||
|
- `mappingMode`:
|
||||||
|
- `'offset'` – store absolute offsets (default, recommended).
|
||||||
|
- `'lengthDiff'` – store mapping as delta relative to previous entries (smaller payloads, but harder to debug). Only use if you profile serialization issues.
|
||||||
|
- `mapRange`: set to `true` if you want `addText` to also produce zero-length mappings (useful when adding sentinel tokens that still need navigation info).
|
||||||
|
|
||||||
|
### Example: Vue Template to TS
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const codeGen = new CodeGen();
|
||||||
|
codeGen.addText('const __ctx = ctx;\n');
|
||||||
|
|
||||||
|
// Map interpolation
|
||||||
|
const expr = templateContent.slice(start, end);
|
||||||
|
codeGen.addText('__ctx.');
|
||||||
|
codeGen.addCode(expr, { start, end }, {
|
||||||
|
navigation: true,
|
||||||
|
semantic: true,
|
||||||
|
completion: true,
|
||||||
|
});
|
||||||
|
codeGen.addText(';\n');
|
||||||
|
```
|
||||||
|
|
||||||
|
Generated text:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const __ctx = ctx;
|
||||||
|
__ctx.user.name;
|
||||||
|
```
|
||||||
|
|
||||||
|
Mappings:
|
||||||
|
|
||||||
|
| Source Range | Generated Range | Flags |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `[start, end]` | `[len('__ctx.') + start2, ...]` | navigation, semantic, completion |
|
||||||
|
|
||||||
|
### Merging CodeGen Output
|
||||||
|
|
||||||
|
`CodeGen` exposes `codeGen.getMappings()` which you can feed directly into your language plugin’s virtual file. When combining multiple code generators (template + script), simply concatenate the text and map offsets accordingly.
|
||||||
|
|
||||||
|
### Performance Tips
|
||||||
|
|
||||||
|
1. **Reuse builders** – instantiate `CodeGen` once per virtual file; avoid nested builders unless you truly have embedded content.
|
||||||
|
2. **Avoid string concatenation** – always use `addText` / `addCode` instead of manual `+=`; `CodeGen` handles buffer growth efficiently.
|
||||||
|
3. **Batch mappings** – if you generate repeated patterns (e.g., loop expansions), precompute mapping templates to minimize overhead.
|
||||||
|
|
||||||
|
## Putting It Together
|
||||||
|
|
||||||
|
A full language plugin typically:
|
||||||
|
|
||||||
|
1. Parses the source (AST, tokens).
|
||||||
|
2. Uses `CodeGen` to emit TypeScript/JavaScript/HTML/CSS virtual documents with precise mappings.
|
||||||
|
3. Exposes the generated text + mappings via the plugin’s `createVirtualCode`.
|
||||||
|
4. (Optional) Provides TypeScript integration, so TS features (diagnostics, completions) run on the generated code.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const fooLanguagePlugin: LanguagePlugin = {
|
||||||
|
getLanguageId(scriptId) {
|
||||||
|
if (scriptId.endsWith('.foo')) return 'foo';
|
||||||
|
},
|
||||||
|
createVirtualCode(scriptId, languageId, snapshot) {
|
||||||
|
const content = snapshot.getText(0, snapshot.getLength());
|
||||||
|
const codeGen = new CodeGen();
|
||||||
|
// ... populate codeGen ...
|
||||||
|
return {
|
||||||
|
id: scriptId + '.ts',
|
||||||
|
languageId: 'typescript',
|
||||||
|
snapshot: ts.ScriptSnapshot.fromString(codeGen.getText()),
|
||||||
|
mappings: codeGen.getMappings(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Source Maps
|
||||||
|
|
||||||
|
- `codeGen.toSourceMap()` – returns a `SourceMap` instance for debugging. Call `map.getMappings()` to inspect raw entries.
|
||||||
|
- Use tools like `source-map-visualizer` (community) to visualize segments.
|
||||||
|
- Log mismatches by running `map.getMappedRange` and verifying the output matches expectations.
|
||||||
|
|
||||||
|
## Best Practices Summary
|
||||||
|
|
||||||
|
1. **Align offsets** – Use `codeGen.getLength()` to track the current generated offset before adding new segments; this ensures mapping ranges start/end at exact positions.
|
||||||
|
2. **Preserve indentation** – When generating multi-line strings, maintain consistent indentation so formatting/renames remain intuitive.
|
||||||
|
3. **Flag-only sections** – If you need completions/rename but no emitted text (e.g., placeholders), use `addMapping` with zero-length ranges and `data` describing the desired capabilities.
|
||||||
|
4. **Embedded file boundaries** – annotate the start/end of embedded files with sentinel comments so you can debug generated output more easily.
|
||||||
|
5. **Unit tests** – snapshot the generated text + mappings; assert that `getMappedRange` and `getSourceRange` round-trip.
|
||||||
|
6. **Schema for data** – store additional metadata (AST nodes, symbol tables) alongside snapshots so service plugins can augment diagnostics without reparsing.
|
||||||
|
7. **Export metadata** – include a `meta` object (custom) on your virtual file describing compile options, feature flags, etc., so language-service plugins can tailor behavior.
|
||||||
|
|
||||||
|
By mastering `@volar/code-gen` and `@volar/source-map`, you can confidently generate virtual documents that preserve every semantic relationship. Accurate mappings are the foundation for precise diagnostics, correct renames, and intuitive editor experiences—invest time in them and the rest of your plugin stack will inherit the payoff.
|
||||||
206
docs/telemetry-and-observability.md
Normal file
206
docs/telemetry-and-observability.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# Telemetry & Observability for Volar Language Servers
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Performance Guide](performance-and-debugging.md) • [Error Handling](error-handling-and-resilience.md)
|
||||||
|
|
||||||
|
Robust observability helps teams detect regressions, diagnose user issues, and justify performance work. This guide covers every capability available to Volar-based servers: telemetry events, logging, progress reporting, health probes, and integration tips for popular editors.
|
||||||
|
|
||||||
|
## Observability Building Blocks
|
||||||
|
|
||||||
|
| Tool | Purpose | APIs |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Console logging | Developer-facing output in editor logs | `connection.console.{info,warn,error}` |
|
||||||
|
| Telemetry events | Structured analytics consumed by clients | `connection.telemetry.logEvent` |
|
||||||
|
| Work done progress | User-facing progress bars for long tasks | `connection.window.createWorkDoneProgress` |
|
||||||
|
| Diagnostics refresh | Force client to re-query diagnostics | `connection.languages.diagnostics.refresh()` |
|
||||||
|
| Custom notifications | Surface warnings/errors to users | `connection.window.showWarningMessage` |
|
||||||
|
|
||||||
|
## Logging Strategies
|
||||||
|
|
||||||
|
### 1. Namespaced Logs
|
||||||
|
|
||||||
|
Prefix logs with your server/component so users can filter easily:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const log = (level: 'info' | 'warn' | 'error', message: string, payload?: unknown) => {
|
||||||
|
const text = `[json-yaml:${level}] ${message}${payload ? ` ${JSON.stringify(payload)}` : ''}`;
|
||||||
|
connection.console[level](text);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use `info` for high-level events (project load, schema cache hits).
|
||||||
|
- Use `warn` for recoverable issues (fallback to default schema).
|
||||||
|
- Use `error` for critical failures—ideally accompanied by a user notification.
|
||||||
|
|
||||||
|
### 2. Log Levels via Settings
|
||||||
|
|
||||||
|
Expose a `logLevel` configuration (`off | error | warn | info | debug`) so users can control verbosity:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
if (config.logLevel === 'debug') {
|
||||||
|
log('info', 'Schema fetch start', { uri });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Structured Payloads
|
||||||
|
|
||||||
|
Include relevant context in JSON to aid parsing:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "...",
|
||||||
|
"event": "schema.fetch",
|
||||||
|
"uri": "http://schemas/foo.json",
|
||||||
|
"durationMs": 123,
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Telemetry Events
|
||||||
|
|
||||||
|
Telemetry is optional and client-controlled; always check capabilities:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const telemetrySupported = server.initializeParams.capabilities?.experimental?.telemetry === true;
|
||||||
|
|
||||||
|
if (telemetrySupported) {
|
||||||
|
connection.telemetry.logEvent({
|
||||||
|
type: 'json-yaml.schemaFetch',
|
||||||
|
uri,
|
||||||
|
durationMs,
|
||||||
|
success,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Design Principles
|
||||||
|
|
||||||
|
1. **No PII** – never include actual source code or user-specific paths unless hashed/anonymized.
|
||||||
|
2. **Actionable** – log events that can drive product decisions (schema fetch failures, TypeScript reloads, “workspace diagnostics took > 5s”).
|
||||||
|
3. **Stable schema** – define event names and payload shapes up front; changing them frequently breaks dashboards.
|
||||||
|
|
||||||
|
### Recommended Events
|
||||||
|
|
||||||
|
| Event | Trigger | Payload |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `schema.fetch` | Schema download (success + failure) | `{ uri, durationMs, success }` |
|
||||||
|
| `diagnostics.publish` | After `sendDiagnostics` | `{ uri, count, durationMs }` |
|
||||||
|
| `workspaceDiagnostics.run` | Workspace diagnostics completed | `{ documentCount, durationMs }` |
|
||||||
|
| `configuration.apply` | New config applied | `{ success, changedKeys }` |
|
||||||
|
| `takeOverMode.warning` | Detected conflicting TS server | `{ message }` |
|
||||||
|
|
||||||
|
### Sample Telemetry Wiring (VS Code Extension)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const telemetryReporter = new TelemetryReporter('volar-extension', version, aiKey);
|
||||||
|
|
||||||
|
connection.telemetry.logEvent = (event) => {
|
||||||
|
telemetryReporter.sendTelemetryEvent(event.type, sanitize(event));
|
||||||
|
};
|
||||||
|
|
||||||
|
function sanitize(event: AnyEvent) {
|
||||||
|
return {
|
||||||
|
...event,
|
||||||
|
uri: undefined, // avoid sending raw file paths
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use Azure App Insights, Segment, or any analytics platform that matches your privacy requirements.
|
||||||
|
- Strip PII (file paths, code snippets) before forwarding events.
|
||||||
|
|
||||||
|
### Dashboard Example
|
||||||
|
|
||||||
|
Track key metrics with a dashboard (e.g., Grafana/Looker):
|
||||||
|
|
||||||
|
| Widget | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| Schema Fetch Success % | `success` count / total by day; alert if < 95%. |
|
||||||
|
| Diagnostics Duration P95 | Box plot of `diagnostics.publish.durationMs`. |
|
||||||
|
| Workspace Diagnostics Runs | Number per workspace; spikes may indicate user confusion. |
|
||||||
|
| Top Error Messages | Grouped count of `takeOverMode.warning` and other errors. |
|
||||||
|
|
||||||
|
Use these dashboards to catch regressions (e.g., schema outages, slow diagnostics) before users report them.
|
||||||
|
|
||||||
|
## Work Done Progress & Notifications
|
||||||
|
|
||||||
|
For long-running operations (initial project load, large workspace diagnostics), display progress so users know work is happening.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function withProgress(title: string, task: () => Promise<void>) {
|
||||||
|
const progress = await connection.window.createWorkDoneProgress();
|
||||||
|
progress.begin(title, 0);
|
||||||
|
try {
|
||||||
|
await task();
|
||||||
|
progress.report(100, 'Complete');
|
||||||
|
} finally {
|
||||||
|
progress.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Also use `connection.window.showWarningMessage` / `showErrorMessage` for issues requiring user intervention (missing schema files, invalid config).
|
||||||
|
|
||||||
|
## Health Checks & Metrics
|
||||||
|
|
||||||
|
Consider exposing internal health metrics for CI or headless environments:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.onRequest('volar/health', async () => ({
|
||||||
|
openDocuments: server.documents.all().length,
|
||||||
|
workspaceFolders: server.workspaceFolders.all.length,
|
||||||
|
lastDiagnosticsMs: metrics.lastDiagnosticsDuration,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
Integrate this request into smoke tests or CI to ensure the server responds with sane values.
|
||||||
|
|
||||||
|
## Editor-Specific Considerations
|
||||||
|
|
||||||
|
### VS Code
|
||||||
|
|
||||||
|
- Use `connection.window.showInformationMessage` sparingly; prefer progress notifications and logs.
|
||||||
|
- Provide commands that surface diagnostics/profiling info for debugging (e.g., “Volar: Show Server Logs”).
|
||||||
|
|
||||||
|
### Neovim
|
||||||
|
|
||||||
|
- Expose an RPC command to toggle verbose logging at runtime.
|
||||||
|
- Append logs to a file (e.g., `~/.cache/volar-nvim/volar.log`) so users can share them in bug reports.
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
- Provide `--log-level` and `--log-file` flags.
|
||||||
|
- For headless usage, print JSON logs to stdout so downstream automation can parse them.
|
||||||
|
|
||||||
|
## Sampling & Rate Limiting
|
||||||
|
|
||||||
|
- For frequent events (per-keystroke completions), sample logs/telemetry to reduce noise:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
if (Math.random() < 0.1) {
|
||||||
|
connection.telemetry.logEvent({ type: 'completion.run', durationMs });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Throttle repeated warnings (e.g., schema fetch failures) to once per URI per minute to avoid spamming logs.
|
||||||
|
|
||||||
|
## Error Reporting
|
||||||
|
|
||||||
|
When an unexpected exception occurs:
|
||||||
|
|
||||||
|
1. Log the stack via `connection.console.error`.
|
||||||
|
2. Emit telemetry (if enabled) with a sanitized version of the error.
|
||||||
|
3. Notify the user when action is needed (“Failed to load schema; see Output ▸ Volar for details”).
|
||||||
|
|
||||||
|
## Observability Checklist
|
||||||
|
|
||||||
|
1. **Logging** – Namespaced helper with log levels; logs include key metadata.
|
||||||
|
2. **Telemetry** – Optional, PII-free events for actions that matter (schema fetch, diagnostics).
|
||||||
|
3. **Progress** – Work done progress notifications for long-running operations.
|
||||||
|
4. **Notifications** – Friendly user messages for actionable issues.
|
||||||
|
5. **Health endpoint** – Optional `volar/health` request for automated monitors.
|
||||||
|
6. **Configurable verbosity** – Users can toggle log level / telemetry participation.
|
||||||
|
7. **Sampling** – Applied to high-frequency events to avoid flooding logs.
|
||||||
|
8. **Documentation** – Tell users how to capture logs/telemetry, attach them to bug reports, and opt out if desired.
|
||||||
|
|
||||||
|
Instrument early and consistently—observability is far easier to add when your server is small than when you’re firefighting production issues.
|
||||||
171
docs/testing-and-ci.md
Normal file
171
docs/testing-and-ci.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# Testing & Continuous Integration for Volar Integrations
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Performance Guide](performance-and-debugging.md) • [LSP Benchmarking](lsp-benchmarking.md)
|
||||||
|
|
||||||
|
High-quality language tooling demands automated verification. This guide outlines every testing strategy—from unit tests that assert source-map correctness to full end-to-end LSP harnesses—plus tips for integrating them into CI pipelines.
|
||||||
|
|
||||||
|
## Test Pyramid Overview
|
||||||
|
|
||||||
|
| Layer | What to test | Tools |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Unit | CodeGen output, source maps, individual plugin hooks | Jest/Vitest/Mocha, snapshot assertions |
|
||||||
|
| Integration | Full language service behaviors (diagnostics, hover, completion) | `vscode-languageserver/node` test harness, custom scripts |
|
||||||
|
| End-to-end | Actual editor clients (VS Code extension, Neovim plugin) | VS Code Extension Tester, Neovim RPC tests, Playwright |
|
||||||
|
|
||||||
|
## Unit Testing Building Blocks
|
||||||
|
|
||||||
|
### CodeGen & Source Map Tests
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { CodeGen } from '@volar/code-gen';
|
||||||
|
|
||||||
|
test('maps interpolation correctly', () => {
|
||||||
|
const codeGen = new CodeGen();
|
||||||
|
codeGen.addText('const ctx = ');
|
||||||
|
codeGen.addCode('foo', { start: 10, end: 13 }, { navigation: true });
|
||||||
|
|
||||||
|
expect(codeGen.getText()).toMatchInlineSnapshot(`
|
||||||
|
"const ctx = foo"
|
||||||
|
`);
|
||||||
|
expect(codeGen.getMappings()).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
data: { navigation: true },
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin Hook Tests
|
||||||
|
|
||||||
|
Mock `TextDocument` instances and call your plugin functions directly:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const doc = TextDocument.create('file://test.foo', 'foo', 0, 'foo: value');
|
||||||
|
const diagnostics = fooServicePlugin.create(context).provideDiagnostics(doc);
|
||||||
|
expect(diagnostics).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"message": "Unknown key: foo",
|
||||||
|
"range": Object {
|
||||||
|
"end": Object { "character": 3, "line": 0 },
|
||||||
|
"start": Object { "character": 0, "line": 0 },
|
||||||
|
},
|
||||||
|
"severity": 2,
|
||||||
|
"source": "foo-plugin",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Testing Language Services
|
||||||
|
|
||||||
|
Create a mini harness that spins up your Volar language service and issues LSP requests.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createConnection } from 'vscode-languageserver/node';
|
||||||
|
import { createServer } from '@volar/language-server/node';
|
||||||
|
|
||||||
|
test('diagnostics for foo files', async () => {
|
||||||
|
const connection = createConnection();
|
||||||
|
const server = createServer(connection);
|
||||||
|
const project = createSimpleProject([fooLanguagePlugin]);
|
||||||
|
|
||||||
|
const diagnosticsPromise = once(connection, 'publishDiagnostics');
|
||||||
|
connection.sendNotification('textDocument/didOpen', {
|
||||||
|
textDocument: { uri: 'file://test.foo', text: 'foo: value', version: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const diagnostics = await diagnosticsPromise;
|
||||||
|
expect(diagnostics.diagnostics).toHaveLength(1);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- Use Node’s `EventEmitter.once` pattern to await specific notifications.
|
||||||
|
- Wrap the connection in a helper that lets you send requests and capture responses from your language service without spinning up a real client.
|
||||||
|
|
||||||
|
## VS Code Extension Testing
|
||||||
|
|
||||||
|
If you ship a VS Code extension:
|
||||||
|
|
||||||
|
1. Use [`@vscode/test-electron`](https://github.com/microsoft/vscode-test) to launch VS Code with your extension + Volar server.
|
||||||
|
2. Prepare a sample workspace under `test-fixtures/`.
|
||||||
|
3. Write integration tests that open files, trigger commands, and inspect diagnostics via VS Code’s extension API.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
suite('Diagnostics smoke test', () => {
|
||||||
|
test('foo file shows diagnostics', async () => {
|
||||||
|
const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(FIXTURE));
|
||||||
|
await vscode.window.showTextDocument(doc);
|
||||||
|
|
||||||
|
await waitForDiagnostics(doc.uri);
|
||||||
|
const diagnostics = vscode.languages.getDiagnostics(doc.uri);
|
||||||
|
assert.strictEqual(diagnostics.length, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Neovim / Other Editor Testing
|
||||||
|
|
||||||
|
- Wrap your server in a CLI (`node dist/server.js`) and use Neovim’s RPC client to send LSP requests.
|
||||||
|
- Use libraries like [`vusted`](https://github.com/notomo/vusted) or `nvim-lspconfig`’s internal test harness for Lua-based tests.
|
||||||
|
|
||||||
|
## Snapshot Testing
|
||||||
|
|
||||||
|
- Use Jest/Vitest snapshots for diagnostics/completions/hovers. Keep fixtures small and redacted to avoid brittle diffs.
|
||||||
|
- When snapshots change, review carefully; schema or plugin updates often alter expected output.
|
||||||
|
|
||||||
|
## Mocking External Dependencies
|
||||||
|
|
||||||
|
- Schema fetchers: stub `fetch`/`readFile` to return deterministic content.
|
||||||
|
- File system watchers: mock `fs.watch` to emit synthetic events.
|
||||||
|
- TypeScript APIs: replace `ts.createLanguageService` with a fake service for unit tests.
|
||||||
|
|
||||||
|
## Continuous Integration Pipeline
|
||||||
|
|
||||||
|
1. **Install dependencies** – `npm ci`.
|
||||||
|
2. **Lint & type-check** – `npm run lint && npm run typecheck`.
|
||||||
|
3. **Unit tests** – `npm test`.
|
||||||
|
4. **Integration tests** – run harness scripts (Node-based) and, if feasible, headless VS Code tests.
|
||||||
|
5. **Bundle check** – build the server (`npm run build`) to ensure TypeScript compilation succeeds.
|
||||||
|
6. **Optional** – run `tsc --noEmit` on your fixtures to ensure `vueCompilerOptions`/`tsconfig` references stay valid.
|
||||||
|
|
||||||
|
### Example GitHub Actions Workflow
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: CI
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run typecheck
|
||||||
|
- run: npm test
|
||||||
|
- run: npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regression Reproduction
|
||||||
|
|
||||||
|
1. Capture the failing file(s) as fixtures and commit them under `test-fixtures/`.
|
||||||
|
2. Add a test that reproduces the issue via the harness (e.g., send `textDocument/hover` and assert the response).
|
||||||
|
3. Fix the bug, ensure the new test passes, and keep the fixture for future regressions.
|
||||||
|
|
||||||
|
## Manual QA Checklist
|
||||||
|
|
||||||
|
Even with robust automated tests, run manual smoke tests before releases:
|
||||||
|
|
||||||
|
1. Open a representative workspace in VS Code with your server extension.
|
||||||
|
2. Verify diagnostics, completions, hovers, rename, code actions, references, and workspace diagnostics.
|
||||||
|
3. Repeat in Neovim or another target editor if you support it.
|
||||||
|
4. Run `vue-tsc --noEmit` (or your CLI equivalent) to ensure TypeScript-based checks align with editor behavior.
|
||||||
|
|
||||||
|
By investing in tests at every layer, your Volar integrations will remain stable as the ecosystem evolves. Automate everything from code-gen mapping checks to full VS Code sessions, and integrate the suite into CI so regressions never reach users.
|
||||||
76
docs/troubleshooting-faq.md
Normal file
76
docs/troubleshooting-faq.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# Troubleshooting & FAQ
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Error Handling](error-handling-and-resilience.md) • [Telemetry](telemetry-and-observability.md)
|
||||||
|
|
||||||
|
This FAQ aggregates the most common VolarJS issues reported by users, along with actionable fixes and references to relevant guides.
|
||||||
|
|
||||||
|
## Duplicate Diagnostics
|
||||||
|
|
||||||
|
**Symptom:** Same error appears twice (one from Volar, one from TypeScript).
|
||||||
|
|
||||||
|
**Fix:** Enable Take Over Mode or disable the built-in TypeScript server.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volar.takeOverMode.enabled": true,
|
||||||
|
"typescript.tsserver.enable": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For Neovim/Sublime, ensure only Volar’s LSP client handles `.ts/.js` when Take Over Mode is on.
|
||||||
|
|
||||||
|
## “Cannot find module '@/…'”
|
||||||
|
|
||||||
|
1. Verify `tsconfig.json` includes `baseUrl` + `paths`.
|
||||||
|
2. Check `Output ▸ Volar` to confirm the intended `tsconfig` is loaded.
|
||||||
|
3. In monorepos, enable project references (`"composite": true`) or add each package tsconfig to the workspace.
|
||||||
|
|
||||||
|
## Schema Fetch Failures / Offline Mode
|
||||||
|
|
||||||
|
- Cache schemas locally (e.g., `~/.cache/volar-schemas`) or bundle them.
|
||||||
|
- Provide a configuration flag like `volarJsonYaml.schemas.allowRemote = false`.
|
||||||
|
- Log failures via `connection.console.warn` so users know which URI failed.
|
||||||
|
|
||||||
|
## Slow Diagnostics
|
||||||
|
|
||||||
|
Checklist:
|
||||||
|
|
||||||
|
1. Increase `VALIDATION_DELAY` (JSON/YAML example) to reduce frequency.
|
||||||
|
2. Disable unused services (Prettier, Emmet) if not required.
|
||||||
|
3. Profile with `VOLAR_TRACE=log` + `node --prof`; see [Performance & Debugging](performance-and-debugging.md).
|
||||||
|
4. Align TypeScript + Volar versions.
|
||||||
|
|
||||||
|
## Take Over Mode Not Activating
|
||||||
|
|
||||||
|
- Disable VS Code’s “TypeScript and JavaScript Language Features” extension.
|
||||||
|
- Set `volar.takeOverMode.enabled = true` and restart.
|
||||||
|
- Ensure `typescript.tsdk` points to the correct TS install if needed.
|
||||||
|
|
||||||
|
## Missing Hover/Completion in Monaco
|
||||||
|
|
||||||
|
- Confirm `@volar/monaco` worker bundle loads (check console).
|
||||||
|
- Ensure TypeScript is available inside the worker (`ts` global).
|
||||||
|
- Version-match server + worker bundles.
|
||||||
|
|
||||||
|
## Neovim “node: command not found”
|
||||||
|
|
||||||
|
- Install Node and set the `cmd` in `lspconfig` to `{ 'node', '/path/to/volar/server.js', '--stdio' }`.
|
||||||
|
- Consider bundling the server as an executable (`pkg`) for users without Node.
|
||||||
|
|
||||||
|
## CLI Always Succeeds
|
||||||
|
|
||||||
|
- Exit with code `1` when diagnostics include severity Error; see [CLI Integration](cli-integration.md).
|
||||||
|
- Provide `--max-warnings` if teams want warning thresholds.
|
||||||
|
|
||||||
|
## Log Locations
|
||||||
|
|
||||||
|
| Editor | Location |
|
||||||
|
| --- | --- |
|
||||||
|
| VS Code | Output ▸ Volar |
|
||||||
|
| Sublime | `View ▸ Show Console` |
|
||||||
|
| Neovim | Plugin-specific log file (`~/.cache/volar/log.txt`) |
|
||||||
|
| CLI | stdout/stderr (support `--log-file`) |
|
||||||
|
|
||||||
|
## Reporting Issues
|
||||||
|
|
||||||
|
Include editor/version, Node/TS/Volar versions, `tsconfig`, reproduction steps, and logs. Reference this FAQ so maintainers can triage quickly.
|
||||||
207
docs/typescript-plugin-deep-dive.md
Normal file
207
docs/typescript-plugin-deep-dive.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# TypeScript Plugin Deep Dive (`@volar/vue-typescript`, `@vue/language-core`)
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Configuration & Projects](configuration-and-projects.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Volar’s superpowers come from its tight integration with TypeScript. This guide explains how the Vue/TypeScript bridge works, what hooks you can customize, and how to extend or troubleshoot it.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
tsserver (TypeScript LS)
|
||||||
|
▲
|
||||||
|
│ ts plugin: @volar/vue-typescript
|
||||||
|
│ - understands .vue files via language plugins
|
||||||
|
│ - surfaces template types to TS
|
||||||
|
│ - provides extra diagnostics/completions
|
||||||
|
│
|
||||||
|
Volar Language Service
|
||||||
|
▲
|
||||||
|
│ @vue/language-core (compiles SFC)
|
||||||
|
│ @volar/typescript (bridge utilities)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Packages
|
||||||
|
|
||||||
|
- `@vue/language-core`: Parses `.vue` SFCs, generates script blocks, template AST, source maps.
|
||||||
|
- `@volar/vue-language-core`: Default Volar language plugin for `.vue`.
|
||||||
|
- `@volar/vue-typescript`: TypeScript plugin that plugs into `tsserver` to expose `.vue` virtual files.
|
||||||
|
- `@volar/typescript`: Shared helpers for creating TS hosts, merging language + TS snapshots.
|
||||||
|
|
||||||
|
## Enabling the TypeScript Plugin
|
||||||
|
|
||||||
|
### VS Code / Volar Extension
|
||||||
|
|
||||||
|
Handled automatically when you install the official Volar extension. To customize:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volar.takeOverMode.enabled": true,
|
||||||
|
"volar.tsPlugin": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Neovim / Other Editors
|
||||||
|
|
||||||
|
Point the language server at the bundled TS plugin:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('lspconfig').volar.setup{
|
||||||
|
init_options = {
|
||||||
|
typescript = {
|
||||||
|
tsdk = '/path/to/node_modules/typescript/lib',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `tsconfig.json`
|
||||||
|
|
||||||
|
Ensure `.vue` files are included and `allowJs` or `allowNonTsExtensions` is enabled:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"allowNonTsExtensions": true,
|
||||||
|
"types": ["vue"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.vue", "src/**/*.ts"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin Internals
|
||||||
|
|
||||||
|
### Language Plugin (`@volar/vue-language-core`)
|
||||||
|
|
||||||
|
Handles:
|
||||||
|
|
||||||
|
- Parsing `<script>`, `<template>`, `<style>` blocks.
|
||||||
|
- Generating virtual TypeScript/JS files with embedded template expressions (`.ts` + `.d.ts`).
|
||||||
|
- Mapping template expressions to TypeScript expressions (for hover, completion, diagnostics).
|
||||||
|
|
||||||
|
Customization Points:
|
||||||
|
|
||||||
|
- `resolveTemplateCompilerOptions`: adjust template compilation (e.g., macros).
|
||||||
|
- `compileTemplate`: override the default compiler for experimental syntax.
|
||||||
|
|
||||||
|
### TypeScript Plugin (`@volar/vue-typescript`)
|
||||||
|
|
||||||
|
Responsibilities:
|
||||||
|
|
||||||
|
- Register `.vue` as a script kind with TS.
|
||||||
|
- Provide virtual files to `tsserver` (`getScriptSnapshot`, `getScriptVersion`).
|
||||||
|
- Inject specialized TS plugin features (two-slash queries, SFC-specific completions).
|
||||||
|
|
||||||
|
Key APIs:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createLanguageServerPlugin } from '@volar/vue-typescript';
|
||||||
|
|
||||||
|
const plugin = createLanguageServerPlugin(ts, { resolveConfig });
|
||||||
|
```
|
||||||
|
|
||||||
|
- `resolveConfig(uri)` – return Vue compiler options (`vueCompilerOptions`) for each workspace folder.
|
||||||
|
- `getExternalFiles(project)` – optionally add extra files (e.g., global components d.ts).
|
||||||
|
|
||||||
|
### Shared Utilities (`@volar/typescript`)
|
||||||
|
|
||||||
|
- `createLanguageServiceHost(ts, sys, language, asScriptId, projectHost)` – builds a TS host aware of Volar language scripts.
|
||||||
|
- `createUriConverter(rootFolders)` – keeps file/URI conversions consistent.
|
||||||
|
- `decorateLanguageServiceHost` – wrap TS host to intercept methods.
|
||||||
|
|
||||||
|
## Customizing Behavior
|
||||||
|
|
||||||
|
### Compiler Options (`vueCompilerOptions`)
|
||||||
|
|
||||||
|
Add to `tsconfig.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vueCompilerOptions": {
|
||||||
|
"target": 3.3,
|
||||||
|
"plugins": ["@vue-macros/volar"],
|
||||||
|
"data": {
|
||||||
|
"useDefineModel": true,
|
||||||
|
"macros": ["defineProps", "defineSlots"]
|
||||||
|
},
|
||||||
|
"experimentalDisableTemplateSupport": false,
|
||||||
|
"extensions": ["vue", "md", "mdx"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notable flags:
|
||||||
|
|
||||||
|
- `target`: matches Vue runtime version; ensures correct emit.
|
||||||
|
- `plugins`: register compiler transformations (macros, legacy compat).
|
||||||
|
- `data.useDefineModel`: enables `<script setup> defineModel`.
|
||||||
|
- `experimentalDisableTemplateSupport`: disable template analysis to speed up script-only workflows.
|
||||||
|
- `extensions`: treat additional file extensions as Vue SFCs.
|
||||||
|
|
||||||
|
### TS Plugin Options
|
||||||
|
|
||||||
|
When loading the plugin manually:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
createLanguageServer({
|
||||||
|
plugins: [
|
||||||
|
vueTypescriptPlugin({
|
||||||
|
resolveConfig: (workspaceUri) => customVueCompilerOptions[workspaceUri],
|
||||||
|
getDocumentRegistry: () => ts.createDocumentRegistry(true),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- `resolveConfig`: supply per-workspace config.
|
||||||
|
- `getDocumentRegistry`: share TS document registry for incremental builds.
|
||||||
|
- `additionalExtensions`: support `.md` or `.html` files as SFCs.
|
||||||
|
|
||||||
|
### Third-Party Macros
|
||||||
|
|
||||||
|
Integrate macros by hooking into the compiler plugin system:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import MyMacroPlugin from 'my-vue-macro/volar';
|
||||||
|
|
||||||
|
vueCompilerOptions: {
|
||||||
|
plugins: [
|
||||||
|
MyMacroPlugin({
|
||||||
|
// plugin-specific options
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diagnostics Control
|
||||||
|
|
||||||
|
- Use `volar.takeOverMode.diagnosticStyle` (VS Code) or `vueCompilerOptions.strictTemplates` to tune template diagnostics.
|
||||||
|
- On the TS side, configure `tsconfig.json` `compilerOptions` (`strict`, `noUnusedLocals`) to control script diagnostics as usual.
|
||||||
|
|
||||||
|
## Debugging Techniques
|
||||||
|
|
||||||
|
1. **`tsserver.log`** – enable via `tsserver.logDirectory` to inspect how Volar registers `.vue` files.
|
||||||
|
2. **`VOLAR_TRACE=log`** – inspect plugin initialization, config resolution.
|
||||||
|
3. **`codeGen.getText()` inspection** – verify generated TS code for templates.
|
||||||
|
4. **TS Server Plugins** – run `tsserver` manually with `--logVerbosity verbose` to trace plugin hooks.
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
| Issue | Cause | Fix |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Duplicate diagnostics | Two TS servers running (built-in + Volar) | Enable Take Over Mode or disable built-in TS extension. |
|
||||||
|
| No template types | `allowJs` / `allowNonTsExtensions` missing | Update `tsconfig`. |
|
||||||
|
| Custom macros undefined | Plugin not added to `vueCompilerOptions.plugins` | Register macro plugin and ensure it’s installed. |
|
||||||
|
| TS version mismatch | Editor uses different TS than CLI | Align `typescript` dependency across workspace and server. |
|
||||||
|
|
||||||
|
## Extending Beyond Vue
|
||||||
|
|
||||||
|
You can build TypeScript plugins for other component formats (e.g., `.svelte`, `.astro`) by:
|
||||||
|
|
||||||
|
1. Implementing a language plugin (virtual code) similar to `@volar/vue-language-core`.
|
||||||
|
2. Wiring it into `@volar/typescript` (custom `createLanguageServiceHost`).
|
||||||
|
3. Providing compiler options/resolvers akin to `vueCompilerOptions`.
|
||||||
|
|
||||||
|
Using the Vue plugin as a reference accelerates custom component language development.
|
||||||
|
|
||||||
|
With a solid understanding of the TypeScript plugin stack, you can tailor Volar’s TS integration for any advanced use case—macros, alternate file extensions, strict template checks, or entirely new component languages.
|
||||||
131
docs/ux-best-practices.md
Normal file
131
docs/ux-best-practices.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# UX Best Practices for Volar-based Language Servers
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Telemetry](telemetry-and-observability.md) • [Error Handling](error-handling-and-resilience.md)
|
||||||
|
|
||||||
|
A language server lives inside the editor experience; thoughtful UX keeps users confident and productive. This guide focuses on user-facing behaviors—status messages, progress indicators, error notifications, settings UX, and documentation—for Volar integrations.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
1. **Transparency** – users know what the server is doing (diagnostics running, schema fetching, errors).
|
||||||
|
2. **Actionable feedback** – every warning/error tells the user how to resolve it.
|
||||||
|
3. **Control** – users can adjust settings, restart the server, or disable features when necessary.
|
||||||
|
|
||||||
|
## Status & Progress Indicators
|
||||||
|
|
||||||
|
### Initial Load / Heavy Tasks
|
||||||
|
|
||||||
|
- Use work-done progress for long-running operations (>500ms).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function withProgress(title: string, task: () => Promise<void>) {
|
||||||
|
const progress = await connection.window.createWorkDoneProgress();
|
||||||
|
progress.begin(title);
|
||||||
|
try {
|
||||||
|
await task();
|
||||||
|
} finally {
|
||||||
|
progress.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- “Volar (JSON/YAML): Indexing workspace…”
|
||||||
|
- “Fetching schemas…”
|
||||||
|
|
||||||
|
### Idle Status
|
||||||
|
|
||||||
|
- Provide a summary command (VS Code command or Neovim RPC) that returns current workspace stats: number of open documents, diagnostics pending, schema cache status.
|
||||||
|
- Show a status bar entry with quick access to commands (e.g., “Volar: Ready • Diagnostics: 12”).
|
||||||
|
|
||||||
|
## Notifications
|
||||||
|
|
||||||
|
Use the right severity for notifications:
|
||||||
|
|
||||||
|
| Scenario | API | Message |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Non-blocking info (feature enabled) | `showInformationMessage` | “Volar Take Over Mode is now active.” |
|
||||||
|
| Recoverable issue | `showWarningMessage` | “Failed to fetch schema. Using cached version.” |
|
||||||
|
| Action required | `showErrorMessage` | “Invalid configuration: volarJsonYaml.schemas must be an array.” |
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- Provide action buttons: `showWarningMessage(msg, 'Retry', 'Open Settings')`.
|
||||||
|
- Limit frequency—use throttling so the same warning doesn’t appear repeatedly.
|
||||||
|
|
||||||
|
## Settings UX
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Publish a markdown file (e.g., `docs/configuration-and-projects.md`) describing every setting with default values and examples.
|
||||||
|
- If shipping a VS Code extension, include configuration defaults in `package.json` with `markdownDescription`.
|
||||||
|
|
||||||
|
### Validation & Feedback
|
||||||
|
|
||||||
|
- When settings are invalid, log the error and notify users with actionable instructions (“Set `volarJsonYaml.schemas` to an array of objects. See docs URL.”).
|
||||||
|
- Provide commands or links that open the relevant settings file (VS Code: `workbench.action.openSettingsJson`).
|
||||||
|
|
||||||
|
### Profiles / Presets
|
||||||
|
|
||||||
|
- Offer easy ways to switch between configurations (commands like “Volar: Use Strict Profile”).
|
||||||
|
- Document the effect of each profile (diagnostic severity, Take Over Mode, etc.).
|
||||||
|
|
||||||
|
## Command Palette & Shortcuts
|
||||||
|
|
||||||
|
Provide commands for common tasks:
|
||||||
|
|
||||||
|
- “Volar: Restart Language Server”
|
||||||
|
- “Volar: Clear Schema Cache”
|
||||||
|
- “Volar: Show Server Logs”
|
||||||
|
- “Volar: Toggle Take Over Mode”
|
||||||
|
|
||||||
|
In Neovim/Sublime, expose equivalents via user commands (`:VolarRestart`, etc.).
|
||||||
|
|
||||||
|
## Error Messaging
|
||||||
|
|
||||||
|
| Bad | Good |
|
||||||
|
| --- | --- |
|
||||||
|
| “Schema fetch failed” | “Schema fetch failed (404). Using cached schema from 2024-05-12. Configure schemas in volarJsonYaml.schemas.” |
|
||||||
|
| “Config invalid” | “Invalid volarJsonYaml.diagnostics.severity: expected string 'error' | 'warn' | 'info'. Reverted to 'warn'. See docs/…” |
|
||||||
|
|
||||||
|
Guidelines:
|
||||||
|
|
||||||
|
- Include cause, fallback action, and next steps.
|
||||||
|
- Link to docs or open configuration automatically when possible.
|
||||||
|
|
||||||
|
## Editor Integration Details
|
||||||
|
|
||||||
|
### VS Code
|
||||||
|
|
||||||
|
- Status bar entry (`window.createStatusBarItem`) with commands.
|
||||||
|
- Register commands for restart/refresh in `package.json`.
|
||||||
|
- Respect VS Code’s log level (use `connection.console` so logs appear in “Output ▸ Volar”).
|
||||||
|
|
||||||
|
### Neovim
|
||||||
|
|
||||||
|
- Provide Lua callbacks to show server status, toggle settings, or display logs.
|
||||||
|
- Write to a log file (`~/.cache/volar/log.txt`) for easy sharing.
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
- Print progress messages (e.g., “Analyzing 120 files…”) with a spinner.
|
||||||
|
- Exit with non-zero status when diagnostics contain errors; print summary at the end.
|
||||||
|
|
||||||
|
## Documentation & Support
|
||||||
|
|
||||||
|
- Maintain a “Known Issues” section (Take Over Mode pitfalls, incompatible extensions).
|
||||||
|
- Provide reproduction steps for common fixes (clearing Vite cache, installing TS 5.x).
|
||||||
|
- Encourage users to attach logs + configuration when filing issues; document how to retrieve them.
|
||||||
|
|
||||||
|
## UX Checklist
|
||||||
|
|
||||||
|
- [ ] Progress indicators for long tasks.
|
||||||
|
- [ ] Status bar/command to show server status.
|
||||||
|
- [ ] Clear, actionable notifications for errors/warnings.
|
||||||
|
- [ ] Settings documented with defaults and examples.
|
||||||
|
- [ ] Commands for restart, cache clearing, log viewing.
|
||||||
|
- [ ] Error messages include cause + next steps.
|
||||||
|
- [ ] Logs accessible via editor output or file.
|
||||||
|
- [ ] CLI/Neovim equivalents for key UX features.
|
||||||
|
|
||||||
|
Thoughtful UX turns a powerful language server into a delightful developer experience. Communicate clearly, surface actionable options, and give users control over how Volar behaves within their editors.
|
||||||
84
docs/vetur-migration.md
Normal file
84
docs/vetur-migration.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Migrating from Vetur to Volar
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Configuration & Projects](configuration-and-projects.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Many Vue projects still rely on Vetur (the legacy VS Code extension). This guide walks through migrating to Volar, highlighting feature differences, configuration updates, and gotchas.
|
||||||
|
|
||||||
|
## Why Switch?
|
||||||
|
|
||||||
|
| Capability | Vetur | Volar |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `<script setup>` support | Limited | Full (type inference, macros) |
|
||||||
|
| Take Over Mode | No | Yes (shared TS server) |
|
||||||
|
| Template type checking | Partial | Full (TS-based) |
|
||||||
|
| Performance | Slower on large projects | Designed for monorepos + incremental diagnostics |
|
||||||
|
|
||||||
|
## Migration Steps
|
||||||
|
|
||||||
|
### 1. Install Volar Extension
|
||||||
|
|
||||||
|
In VS Code, install “Vue Language Features (Volar)” and disable Vetur:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vetur.validation.template": false,
|
||||||
|
"vetur.completion.autoImport": false,
|
||||||
|
"vetur.experimental.templateInterpolationService": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or disable the entire Vetur extension via the Extensions panel.
|
||||||
|
|
||||||
|
### 2. Enable Take Over Mode (Optional)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volar.takeOverMode.enabled": true,
|
||||||
|
"volar.tsPlugin": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Disable VS Code’s built-in TypeScript server to avoid duplicate diagnostics.
|
||||||
|
|
||||||
|
### 3. Update `tsconfig.json`
|
||||||
|
|
||||||
|
- Include `.vue` files (`"include": ["src/**/*.vue", "src/**/*.ts"]`).
|
||||||
|
- Set `allowJs` and `allowNonTsExtensions` to `true`.
|
||||||
|
- Add `"types": ["vue"]`.
|
||||||
|
|
||||||
|
### 4. Remove Vetur-specific Settings
|
||||||
|
|
||||||
|
- Delete `.vscode/settings.json` entries for `vetur.*`.
|
||||||
|
- Replace Vetur formatting with Prettier or `volar-service-prettier`.
|
||||||
|
|
||||||
|
### 5. Verify Diagnostics
|
||||||
|
|
||||||
|
Open representative `.vue` files and confirm template diagnostics, completions, hover, and go-to-definition work as expected.
|
||||||
|
|
||||||
|
### 6. Update CI/CLI
|
||||||
|
|
||||||
|
- Replace `vti diagnostics` or Vetur-specific scripts with `vue-tsc --noEmit` or a Volar CLI ([CLI Integration](cli-integration.md)).
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
| Issue | Fix |
|
||||||
|
| --- | --- |
|
||||||
|
| Duplicate diagnostics | Disable Vetur + built-in TS server; enable Take Over Mode. |
|
||||||
|
| Missing `<i18n>` completions | Install `volar-service-json` or vue-i18n Volar plugin. |
|
||||||
|
| Formatting changed | Configure Prettier or install `volar-service-prettier`. |
|
||||||
|
| Legacy Vue 2 project | Use Volar’s compatibility mode (`vueCompilerOptions.target = 2.7`) or keep Vetur until upgrade. |
|
||||||
|
|
||||||
|
## Advanced Tips
|
||||||
|
|
||||||
|
- **Auto Switch Prompt**: Set `"volar.autoSwitchToTakeOverMode": true` to prompt users when features require Take Over Mode.
|
||||||
|
- **Monorepos**: Use multi-root workspaces so each package loads its own `tsconfig`—Vetur often guessed, Volar requires explicit configs.
|
||||||
|
- **Telemetry**: Emit a warning when the server detects Vetur still running to encourage full migration.
|
||||||
|
|
||||||
|
### Verification Checklist
|
||||||
|
|
||||||
|
1. `<script setup>` macros show inferred types.
|
||||||
|
2. Template completions list component props/slots.
|
||||||
|
3. Diagnostics align between editors and CI.
|
||||||
|
4. No duplicate errors.
|
||||||
|
|
||||||
|
Document these steps in your project README to help contributors migrate smoothly.
|
||||||
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.
|
||||||
100
docs/volar-service-catalog.md
Normal file
100
docs/volar-service-catalog.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Volar Service Catalog (`volar-service-*`)
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Plugin Authoring](plugin-authoring.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Volar services (`volar-service-*`) are language-service plugins that extend Volar with extra capabilities—formatting, Emmet support, CSS completions, etc. This catalog documents the most common services, how to configure them, and how to evaluate compatibility for custom stacks.
|
||||||
|
|
||||||
|
## How Services Plug In
|
||||||
|
|
||||||
|
Services implement the `LanguageServicePlugin` interface and are typically registered in two places:
|
||||||
|
|
||||||
|
1. **Language Server** (`@volar/language-server`): pass them in when creating the project.
|
||||||
|
2. **Client Configuration**: give users toggles (enable/disable, options).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createLanguageService } from '@volar/language-service';
|
||||||
|
import volarServiceEmmet from 'volar-service-emmet';
|
||||||
|
import volarServicePrettier from 'volar-service-prettier';
|
||||||
|
|
||||||
|
const service = createLanguageService(language, [volarServiceEmmet(), volarServicePrettier()], env, project);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Official Services
|
||||||
|
|
||||||
|
| Package | Capability | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `volar-service-css` | CSS/SCSS/Less completions, diagnostics, hover in `<style>` blocks | Configurable via `languages` + `scopedModules`. |
|
||||||
|
| `volar-service-emmet` | Emmet abbreviations inside template/style regions | Enable per workspace; configurable snippet profiles. |
|
||||||
|
| `volar-service-html` | HTML attribute/tag completions for templates | Works alongside Vue component completion; helpful for custom elements. |
|
||||||
|
| `volar-service-json` | JSON completions/hover for `<i18n>` blocks | Targets embedded JSON sections. |
|
||||||
|
| `volar-service-prettier` | Formatting for templates/scripts/styles via Prettier | Respects `.prettierrc`; expose settings for `singleQuote`, `semi`, etc. |
|
||||||
|
| `volar-service-typescript` | TypeScript-based completions/diagnostics | Powers TS-specific features (import completions, quick fixes). |
|
||||||
|
| `volar-service-typescript-twoslash-queries` | Inline `^?` twoslash queries (type exploration) | Great for REPLs/docs; disable in production editors if noise. |
|
||||||
|
| `volar-service-pug` / `volar-service-pug-beautify` | Pug template support + formatting | Use when `<template lang="pug">`. |
|
||||||
|
|
||||||
|
All official packages live under [`vuejs/language-tools`](https://github.com/vuejs/language-tools/tree/master/packages).
|
||||||
|
|
||||||
|
## Community Services (Examples)
|
||||||
|
|
||||||
|
| Package | Capability | Maintainer |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [`@volar-plugins/tailwind`](https://github.com/windicss/windicss-intellisense) | Tailwind class name completions/diagnostics | Windi/Tailwind community |
|
||||||
|
| [`@volar-plugins/i18n`](https://github.com/intlify/vue-i18n-next/tree/master/packages/volar-plugin-vue-i18n) | Vue I18n completions + diagnostics | Intlify |
|
||||||
|
| [`@vue-macros/volar`](https://github.com/sxzz/vue-macros/tree/main/packages/volar) | Macro support (defineSlots, betterDefine) | Vue Macros |
|
||||||
|
| [`volar-service-astro`](https://github.com/snowpackjs/astro-language-tools) (conceptual) | Astro component support (example patterns) | Astro community |
|
||||||
|
|
||||||
|
When adopting community services:
|
||||||
|
|
||||||
|
- Audit compatibility with your Volar version (check peer dependencies).
|
||||||
|
- Review configuration options—they often expose toggles for strictness or frameworks.
|
||||||
|
|
||||||
|
## Configuration Pattern
|
||||||
|
|
||||||
|
Expose service-specific settings via your LSP server:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type MyConfig = {
|
||||||
|
emmet?: {
|
||||||
|
enabled: boolean;
|
||||||
|
showExpandedAbbreviation?: boolean;
|
||||||
|
};
|
||||||
|
prettier?: {
|
||||||
|
semi?: boolean;
|
||||||
|
singleQuote?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const services = [
|
||||||
|
volarServiceEmmet(() => config.emmet ?? { enabled: true }),
|
||||||
|
volarServicePrettier(() => config.prettier),
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
On config changes, re-create the service or update its options.
|
||||||
|
|
||||||
|
## Compatibility Matrix
|
||||||
|
|
||||||
|
| Service | Works With | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| CSS/HTML/JSON | All editors (VS Code, Neovim, Sublime) | No extra dependencies. |
|
||||||
|
| Prettier | Requires Prettier dependency in workspace or bundled version | Provide fallback if Prettier missing. |
|
||||||
|
| Emmet | Works best in editors with Emmet disabled (to avoid double expansion) | Document instructions for VS Code (“disable built-in Emmet for Vue files”). |
|
||||||
|
| Tailwind/Windi | Requires project-level config (`tailwind.config.js`) | Cache parse results across files. |
|
||||||
|
| I18n | Requires Vue I18n dependency; scans locale files | Provide configuration for locale file globs. |
|
||||||
|
|
||||||
|
## Service Development Tips
|
||||||
|
|
||||||
|
1. **Start from templates** – use official services as references; they showcase best practices.
|
||||||
|
2. **Split TS vs LSP logic** – if your service manipulates TS AST, keep code in a separate module so it can be reused in TS plugins.
|
||||||
|
3. **Provide diagnostics source** – set `diagnostic.source = 'your-service'` so users can filter.
|
||||||
|
4. **Respect cancellation tokens** – service hooks may run frequently; bail out if `token.isCancellationRequested`.
|
||||||
|
5. **Expose config** – allow users to toggle features to avoid conflicts (e.g., Emmet vs built-in Emmet).
|
||||||
|
|
||||||
|
## Discoverability
|
||||||
|
|
||||||
|
- Monitor the [Volar Discussions](https://github.com/vuejs/language-tools/discussions) and [Awesome Volar](https://github.com/johnsoncodehk/awesome-volar) (if maintained) for new services.
|
||||||
|
- Encourage plugin authors to publish README snippets describing how to register the service with LSP clients (settings JSON, init options).
|
||||||
|
|
||||||
|
With this catalog and configuration guidance, you can confidently assemble the right mix of Volar services—or build your own—to deliver rich editor experiences tailored to your stack.
|
||||||
173
docs/workspace-diagnostics.md
Normal file
173
docs/workspace-diagnostics.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# Workspace Diagnostics Deep Dive
|
||||||
|
|
||||||
|
> [Docs Index](README.md) • [Repo README](../README.md) • [Performance Guide](performance-and-debugging.md) • [Testing & CI](testing-and-ci.md)
|
||||||
|
|
||||||
|
Workspace diagnostics allow clients to request a consolidated snapshot of diagnostics across every tracked document. Volar includes built-in support, but when you author custom servers or plugins you may need to understand the full protocol to extend or debug it. This guide covers request/response semantics, result IDs, incremental updates, and best practices.
|
||||||
|
|
||||||
|
## Why Workspace Diagnostics?
|
||||||
|
|
||||||
|
- Provide a single “Problems” list for projects with hundreds of files without opening each file.
|
||||||
|
- Enable CLI tooling (`volar check`) to return the same diagnostics as the editor.
|
||||||
|
- Support `workspace/diagnostic` clients (VS Code Insiders, Neovim, Sublime LSP) that fetch diagnostics on demand.
|
||||||
|
|
||||||
|
## Protocol Primer
|
||||||
|
|
||||||
|
### Request: `workspace/diagnostic`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (params, token) => {
|
||||||
|
// params.previousResultIds – known results from last run
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `identifier`: optional string set by the client during registration; echo it back in responses if present.
|
||||||
|
- `previousResultIds`: array of `{ uri: string, value: string }` – tells the server what diagnostics the client already has.
|
||||||
|
|
||||||
|
### Response: `WorkspaceDiagnosticReport`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type WorkspaceDiagnosticReport = {
|
||||||
|
items: WorkspaceDocumentDiagnosticReport[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type WorkspaceDocumentDiagnosticReport =
|
||||||
|
| WorkspaceFullDocumentDiagnosticReport
|
||||||
|
| WorkspaceUnchangedDocumentDiagnosticReport;
|
||||||
|
|
||||||
|
type WorkspaceFullDocumentDiagnosticReport = {
|
||||||
|
kind: 'full';
|
||||||
|
uri: string;
|
||||||
|
version: number | null;
|
||||||
|
items: Diagnostic[];
|
||||||
|
resultId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WorkspaceUnchangedDocumentDiagnosticReport = {
|
||||||
|
kind: 'unchanged';
|
||||||
|
uri: string;
|
||||||
|
version: number | null;
|
||||||
|
resultId: string;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- `resultId`: opaque string used to identify a diagnostic result. If you send `kind: 'unchanged'`, you must supply the previous `resultId`.
|
||||||
|
|
||||||
|
## Implementation Patterns
|
||||||
|
|
||||||
|
### 1. Full Refresh Every Time
|
||||||
|
|
||||||
|
Simplest approach (used in our JSON/YAML example):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (_params, token) => {
|
||||||
|
const items = [];
|
||||||
|
for (const doc of server.documents.all()) {
|
||||||
|
if (token.isCancellationRequested) break;
|
||||||
|
items.push({
|
||||||
|
uri: doc.uri,
|
||||||
|
kind: DocumentDiagnosticReportKind.Full,
|
||||||
|
version: doc.version ?? null,
|
||||||
|
items: await collectDiagnostics(doc),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { items };
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- Pros: Easy to implement, no result IDs to track.
|
||||||
|
- Cons: Potentially expensive for large workspaces; clients will reprocess every diagnostic each run.
|
||||||
|
|
||||||
|
### 2. Incremental with `resultId`
|
||||||
|
|
||||||
|
Track diagnostics per document:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const workspaceResults = new Map<string, { resultId: string; items: Diagnostic[] }>();
|
||||||
|
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (params, token) => {
|
||||||
|
const items: WorkspaceDocumentDiagnosticReport[] = [];
|
||||||
|
for (const doc of server.documents.all()) {
|
||||||
|
if (token.isCancellationRequested) break;
|
||||||
|
|
||||||
|
const cached = workspaceResults.get(doc.uri);
|
||||||
|
const previousResultId = params.previousResultIds?.find((p) => p.uri === doc.uri)?.value;
|
||||||
|
|
||||||
|
if (cached && cached.resultId === previousResultId && !doc.versionChangedSince(cached.resultId)) {
|
||||||
|
items.push({
|
||||||
|
uri: doc.uri,
|
||||||
|
kind: 'unchanged',
|
||||||
|
version: doc.version ?? null,
|
||||||
|
resultId: cached.resultId,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diagnostics = await collectDiagnostics(doc);
|
||||||
|
const resultId = crypto.randomUUID();
|
||||||
|
workspaceResults.set(doc.uri, { resultId, items: diagnostics });
|
||||||
|
items.push({
|
||||||
|
uri: doc.uri,
|
||||||
|
kind: 'full',
|
||||||
|
version: doc.version ?? null,
|
||||||
|
items: diagnostics,
|
||||||
|
resultId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { items };
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- Pros: Clients skip reprocessing unchanged documents; faster on subsequent runs.
|
||||||
|
- Cons: Requires caching `resultId` + diagnostics and invalidating cache when files change.
|
||||||
|
|
||||||
|
### When to Invalidate
|
||||||
|
|
||||||
|
1. Document opened/changed – remove cached entry and recompute on next workspace request.
|
||||||
|
2. Configuration change (e.g., schema update) – bump all `resultId`s to ensure clients refresh.
|
||||||
|
3. Project reload – clear cache entirely.
|
||||||
|
|
||||||
|
## Progress & Partial Results
|
||||||
|
|
||||||
|
The protocol supports work-done progress and partial results. To implement:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (params, token, _workDone, resultProgress) => {
|
||||||
|
for (const doc of server.documents.all()) {
|
||||||
|
if (token.isCancellationRequested) break;
|
||||||
|
const report = ...buildReport(doc)...;
|
||||||
|
resultProgress?.report({ items: [report] });
|
||||||
|
}
|
||||||
|
return { items: [] }; // or final aggregated array
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Use partial results for very large workspaces so clients receive incremental updates instead of waiting for completion.
|
||||||
|
|
||||||
|
## Volar Built-in Behavior
|
||||||
|
|
||||||
|
`@volar/language-server` already implements workspace diagnostics for TypeScript-based projects when `workspaceDiagnostics` capability is enabled. If you use `createTypeScriptProject`, you get:
|
||||||
|
|
||||||
|
- Automatic `resultId` management.
|
||||||
|
- Honor for `previousResultIds`.
|
||||||
|
- Partial result streaming.
|
||||||
|
|
||||||
|
However, if you build custom projects (e.g., JSON/YAML server) you need to implement the handler yourself, as shown above.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Version numbers** – include the document’s version (or `null` if unknown). Helps clients detect stale reports.
|
||||||
|
2. **Respect cancellation** – workspace diagnostics can be expensive; break when the token is canceled.
|
||||||
|
3. **Throttle frequency** – only run workspace diagnostics on explicit requests or when the user triggers a “Refresh” command. Avoid running them on every change by default.
|
||||||
|
4. **Surface progress** – pair workspace diagnostics with a work-done progress notification so users see progress for large workspaces.
|
||||||
|
5. **Log durations** – record how long workspace diagnostics take; emit telemetry (`workspaceDiagnostics.run`) to spot regressions.
|
||||||
|
6. **Tie into CLI** – reuse the same diagnostics pipeline for CLI tools (`volar check`) to ensure consistent results.
|
||||||
|
|
||||||
|
## Debugging Workspace Diagnostics
|
||||||
|
|
||||||
|
- Enable protocol tracing (`VOLAR_TRACE=protocol`) and watch `workspace/diagnostic` traffic.
|
||||||
|
- Verify `resultId` usage: when the client provides previous IDs, ensure you respond with `kind: 'unchanged'` if nothing changed.
|
||||||
|
- Check that `collectDiagnostics` returns the same results as `textDocument/publishDiagnostics`; any divergence indicates a caching or mapping issue.
|
||||||
|
|
||||||
|
By understanding the workspace diagnostics protocol and building on Volar’s primitives, you can provide lightning-fast, up-to-date diagnostic summaries for any workspace size, both in editors and in automated tooling.
|
||||||
47
examples/json-yaml-lsp/README.md
Normal file
47
examples/json-yaml-lsp/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# JSON/YAML LSP Example
|
||||||
|
|
||||||
|
Minimal language server that combines Volar’s Vue intelligence with the JSON and YAML language services. It mirrors the steps in `docs/building-lsp-json-yaml.md` and gives you a runnable playground.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev # starts the LSP over stdio
|
||||||
|
```
|
||||||
|
|
||||||
|
`npm run build && npm start` compiles to `dist/` and runs the emitted JavaScript.
|
||||||
|
|
||||||
|
Use VS Code’s “Run Extension” launch config or any LSP-compatible client and point it at `node ./dist/server.js` (stdio). The server advertises completions, hovers, and diagnostics for `.vue`, `.json`, and `.yaml` files.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `src/jsonYaml.ts` – wraps the JSON and YAML language services with lightweight helpers, schema caching, dynamic configuration, code actions, and definition helpers.
|
||||||
|
- `src/server.ts` – entry point that wires routing, diagnostics, completion/hover/definition/code-action handlers, configuration reloads, and cancellation-aware plumbing on top of Volar’s connection utilities.
|
||||||
|
|
||||||
|
## Feature Highlights
|
||||||
|
|
||||||
|
- **Diagnostics + auto-fixes**: validations are debounced and version-gated; YAML code actions come straight from `yaml-language-server` and JSON documents offer a quick fix to add a `$schema` entry (configurable via settings).
|
||||||
|
- **Hover & completion context**: handlers pass every request through the structured services, ensuring schema-derived detail bubbles up in tooltips and suggestions.
|
||||||
|
- **Follow `$ref` definitions**: definition requests are routed to the JSON/YAML services so `$ref` targets and YAML anchors resolve to their sources.
|
||||||
|
- **Workspace-aware schemas**: relative schema URLs are resolved via `server.workspaceFolders`, so configs like `./schemas/service.schema.json` just work.
|
||||||
|
- **Workspace diagnostics**: implements `languages.diagnostics.onWorkspace`, letting supporting editors pull batched diagnostic snapshots for all tracked files.
|
||||||
|
|
||||||
|
Configure behaviour from your editor by sending the `volarJsonYaml` setting:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"volarJsonYaml": {
|
||||||
|
"json": {
|
||||||
|
"defaultSchemaUri": "./schemas/service.schema.json",
|
||||||
|
"schemas": [
|
||||||
|
{ "fileMatch": ["config/*.json"], "url": "./schemas/service.schema.json" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"customTags": ["!secret scalar"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can extend this sample by adding more `volar-service-*` plugins or extra third-party backends (e.g., XML). Refer to the troubleshooting tips in the main docs if routing behaves unexpectedly.
|
||||||
876
examples/json-yaml-lsp/package-lock.json
generated
Normal file
876
examples/json-yaml-lsp/package-lock.json
generated
Normal file
@@ -0,0 +1,876 @@
|
|||||||
|
{
|
||||||
|
"name": "@volar-docs/json-yaml-lsp-example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@volar-docs/json-yaml-lsp-example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-server": "^2.2.5",
|
||||||
|
"vscode-json-languageservice": "^5.3.7",
|
||||||
|
"vscode-languageserver": "^9.0.1",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"yaml-language-server": "^1.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node10": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node12": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node14": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node16": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz",
|
||||||
|
"integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@volar/language-core": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/source-map": "2.4.23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@volar/language-server": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-k0iO+tybMGMMyrNdWOxgFkP0XJTdbH0w+WZlM54RzJU3WZSjHEupwL30klpM7ep4FO6qyQa03h+VcGHD4Q8gEg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"@volar/language-service": "2.4.23",
|
||||||
|
"@volar/typescript": "2.4.23",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"request-light": "^0.7.0",
|
||||||
|
"vscode-languageserver": "^9.0.1",
|
||||||
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@volar/language-server/node_modules/request-light": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@volar/language-service": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-h5mU9DZ/6u3LCB9xomJtorNG6awBNnk9VuCioGsp6UtFiM8amvS5FcsaC3dabdL9zO0z+Gq9vIEMb/5u9K6jGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@volar/source-map": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@volar/typescript": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vscode/l10n": {
|
||||||
|
"version": "0.0.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz",
|
||||||
|
"integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
|
||||||
|
},
|
||||||
|
"node_modules/acorn-walk": {
|
||||||
|
"version": "8.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": "^8.11.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/acorn-walk/node_modules/acorn": {
|
||||||
|
"version": "8.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ajv": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ajv-draft-04": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"ajv": "^8.5.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"ajv": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/create-require": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||||
|
},
|
||||||
|
"node_modules/jsonc-parser": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"node_modules/make-error": {
|
||||||
|
"version": "1.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/path-browserify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/request-light": {
|
||||||
|
"version": "0.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz",
|
||||||
|
"integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="
|
||||||
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ts-node": {
|
||||||
|
"version": "10.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.2",
|
||||||
|
"acorn": "^8.4.1",
|
||||||
|
"acorn-walk": "^8.1.1",
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"create-require": "^1.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"v8-compile-cache-lib": "^3.0.1",
|
||||||
|
"yn": "3.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ts-node": "dist/bin.js",
|
||||||
|
"ts-node-cwd": "dist/bin-cwd.js",
|
||||||
|
"ts-node-esm": "dist/bin-esm.js",
|
||||||
|
"ts-node-script": "dist/bin-script.js",
|
||||||
|
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||||
|
"ts-script": "dist/bin-script-deprecated.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": ">=1.2.50",
|
||||||
|
"@swc/wasm": ">=1.2.50",
|
||||||
|
"@types/node": "*",
|
||||||
|
"typescript": ">=2.7"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/wasm": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ts-node/node_modules/acorn": {
|
||||||
|
"version": "8.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/v8-compile-cache-lib": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/vscode-json-languageservice": {
|
||||||
|
"version": "5.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.6.3.tgz",
|
||||||
|
"integrity": "sha512-UDF7sJF5t7mzUzXL6dsClkvnHS4xnDL/gOMKGQiizRHmswlk/xSPGZxEvAtszWQF0ImNcJ0j9l+rHuefGzit1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vscode/l10n": "^0.0.18",
|
||||||
|
"jsonc-parser": "^3.3.1",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.12",
|
||||||
|
"vscode-languageserver-types": "^3.17.5",
|
||||||
|
"vscode-uri": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-jsonrpc": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-languageserver-protocol": "3.17.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"installServerIntoExtension": "bin/installServerIntoExtension"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-protocol": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-jsonrpc": "8.2.0",
|
||||||
|
"vscode-languageserver-types": "3.17.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-textdocument": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-types": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
|
||||||
|
},
|
||||||
|
"node_modules/vscode-nls": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="
|
||||||
|
},
|
||||||
|
"node_modules/vscode-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="
|
||||||
|
},
|
||||||
|
"node_modules/yaml": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yaml-language-server": {
|
||||||
|
"version": "1.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.19.2.tgz",
|
||||||
|
"integrity": "sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vscode/l10n": "^0.0.18",
|
||||||
|
"ajv": "^8.17.1",
|
||||||
|
"ajv-draft-04": "^1.0.0",
|
||||||
|
"lodash": "4.17.21",
|
||||||
|
"prettier": "^3.5.0",
|
||||||
|
"request-light": "^0.5.7",
|
||||||
|
"vscode-json-languageservice": "4.1.8",
|
||||||
|
"vscode-languageserver": "^9.0.0",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.1",
|
||||||
|
"vscode-languageserver-types": "^3.16.0",
|
||||||
|
"vscode-uri": "^3.0.2",
|
||||||
|
"yaml": "2.7.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"yaml-language-server": "bin/yaml-language-server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yaml-language-server/node_modules/vscode-json-languageservice": {
|
||||||
|
"version": "4.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz",
|
||||||
|
"integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"jsonc-parser": "^3.0.0",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.1",
|
||||||
|
"vscode-languageserver-types": "^3.16.0",
|
||||||
|
"vscode-nls": "^5.0.0",
|
||||||
|
"vscode-uri": "^3.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@cspotcode/source-map-support": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@tsconfig/node10": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node12": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node14": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node16": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "24.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz",
|
||||||
|
"integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@volar/language-core": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==",
|
||||||
|
"requires": {
|
||||||
|
"@volar/source-map": "2.4.23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@volar/language-server": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-k0iO+tybMGMMyrNdWOxgFkP0XJTdbH0w+WZlM54RzJU3WZSjHEupwL30klpM7ep4FO6qyQa03h+VcGHD4Q8gEg==",
|
||||||
|
"requires": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"@volar/language-service": "2.4.23",
|
||||||
|
"@volar/typescript": "2.4.23",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"request-light": "^0.7.0",
|
||||||
|
"vscode-languageserver": "^9.0.1",
|
||||||
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"request-light": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@volar/language-service": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-h5mU9DZ/6u3LCB9xomJtorNG6awBNnk9VuCioGsp6UtFiM8amvS5FcsaC3dabdL9zO0z+Gq9vIEMb/5u9K6jGQ==",
|
||||||
|
"requires": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@volar/source-map": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q=="
|
||||||
|
},
|
||||||
|
"@volar/typescript": {
|
||||||
|
"version": "2.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz",
|
||||||
|
"integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==",
|
||||||
|
"requires": {
|
||||||
|
"@volar/language-core": "2.4.23",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@vscode/l10n": {
|
||||||
|
"version": "0.0.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz",
|
||||||
|
"integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
|
||||||
|
},
|
||||||
|
"acorn-walk": {
|
||||||
|
"version": "8.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn": "^8.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "8.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv-draft-04": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"arg": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"create-require": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
|
},
|
||||||
|
"fast-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||||
|
},
|
||||||
|
"jsonc-parser": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"make-error": {
|
||||||
|
"version": "1.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"path-browserify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="
|
||||||
|
},
|
||||||
|
"request-light": {
|
||||||
|
"version": "0.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz",
|
||||||
|
"integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="
|
||||||
|
},
|
||||||
|
"require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
|
||||||
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"version": "10.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.2",
|
||||||
|
"acorn": "^8.4.1",
|
||||||
|
"acorn-walk": "^8.1.1",
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"create-require": "^1.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"v8-compile-cache-lib": "^3.0.1",
|
||||||
|
"yn": "3.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "8.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"v8-compile-cache-lib": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"vscode-json-languageservice": {
|
||||||
|
"version": "5.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.6.3.tgz",
|
||||||
|
"integrity": "sha512-UDF7sJF5t7mzUzXL6dsClkvnHS4xnDL/gOMKGQiizRHmswlk/xSPGZxEvAtszWQF0ImNcJ0j9l+rHuefGzit1w==",
|
||||||
|
"requires": {
|
||||||
|
"@vscode/l10n": "^0.0.18",
|
||||||
|
"jsonc-parser": "^3.3.1",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.12",
|
||||||
|
"vscode-languageserver-types": "^3.17.5",
|
||||||
|
"vscode-uri": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vscode-jsonrpc": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="
|
||||||
|
},
|
||||||
|
"vscode-languageserver": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
|
||||||
|
"requires": {
|
||||||
|
"vscode-languageserver-protocol": "3.17.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vscode-languageserver-protocol": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||||
|
"requires": {
|
||||||
|
"vscode-jsonrpc": "8.2.0",
|
||||||
|
"vscode-languageserver-types": "3.17.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vscode-languageserver-textdocument": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="
|
||||||
|
},
|
||||||
|
"vscode-languageserver-types": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
|
||||||
|
},
|
||||||
|
"vscode-nls": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="
|
||||||
|
},
|
||||||
|
"vscode-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="
|
||||||
|
},
|
||||||
|
"yaml-language-server": {
|
||||||
|
"version": "1.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.19.2.tgz",
|
||||||
|
"integrity": "sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==",
|
||||||
|
"requires": {
|
||||||
|
"@vscode/l10n": "^0.0.18",
|
||||||
|
"ajv": "^8.17.1",
|
||||||
|
"ajv-draft-04": "^1.0.0",
|
||||||
|
"lodash": "4.17.21",
|
||||||
|
"prettier": "^3.5.0",
|
||||||
|
"request-light": "^0.5.7",
|
||||||
|
"vscode-json-languageservice": "4.1.8",
|
||||||
|
"vscode-languageserver": "^9.0.0",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.1",
|
||||||
|
"vscode-languageserver-types": "^3.16.0",
|
||||||
|
"vscode-uri": "^3.0.2",
|
||||||
|
"yaml": "2.7.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-json-languageservice": {
|
||||||
|
"version": "4.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz",
|
||||||
|
"integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==",
|
||||||
|
"requires": {
|
||||||
|
"jsonc-parser": "^3.0.0",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.1",
|
||||||
|
"vscode-languageserver-types": "^3.16.0",
|
||||||
|
"vscode-nls": "^5.0.0",
|
||||||
|
"vscode-uri": "^3.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
examples/json-yaml-lsp/package.json
Normal file
22
examples/json-yaml-lsp/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@volar-docs/json-yaml-lsp-example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "ts-node src/server.ts",
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"start": "node dist/server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-server": "^2.2.5",
|
||||||
|
"vscode-json-languageservice": "^5.3.7",
|
||||||
|
"vscode-languageserver": "^9.0.1",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.11",
|
||||||
|
"yaml-language-server": "^1.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
224
examples/json-yaml-lsp/src/jsonYaml.ts
Normal file
224
examples/json-yaml-lsp/src/jsonYaml.ts
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import type {
|
||||||
|
CodeAction,
|
||||||
|
CodeActionParams,
|
||||||
|
CompletionList,
|
||||||
|
DefinitionLink,
|
||||||
|
Diagnostic,
|
||||||
|
Hover,
|
||||||
|
Position,
|
||||||
|
} from 'vscode-languageserver';
|
||||||
|
import { CodeActionKind } from 'vscode-languageserver';
|
||||||
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
import {
|
||||||
|
getLanguageService as createJsonLanguageService,
|
||||||
|
type JSONDocument,
|
||||||
|
type LanguageSettings as JsonLanguageSettings,
|
||||||
|
} from 'vscode-json-languageservice';
|
||||||
|
import {
|
||||||
|
getLanguageService as createYamlLanguageService,
|
||||||
|
type LanguageSettings as YamlLanguageSettings,
|
||||||
|
} from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService.js';
|
||||||
|
|
||||||
|
type SchemaFetcher = (uri: string) => Promise<string>;
|
||||||
|
|
||||||
|
interface ExtendedJsonSettings extends JsonLanguageSettings {
|
||||||
|
defaultSchemaUri?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JsonYamlConfiguration {
|
||||||
|
json?: ExtendedJsonSettings;
|
||||||
|
yaml?: YamlLanguageSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JsonYamlServices {
|
||||||
|
validateJson(document: TextDocument): Promise<Diagnostic[]>;
|
||||||
|
validateYaml(document: TextDocument): Promise<Diagnostic[]>;
|
||||||
|
completeJson(document: TextDocument, position: Position): Promise<CompletionList | null>;
|
||||||
|
completeYaml(document: TextDocument, position: Position): Promise<CompletionList | null>;
|
||||||
|
hoverJson(document: TextDocument, position: Position): Promise<Hover | null>;
|
||||||
|
hoverYaml(document: TextDocument, position: Position): Promise<Hover | null>;
|
||||||
|
configure(settings: JsonYamlConfiguration): void;
|
||||||
|
definitionJson(document: TextDocument, position: Position): Promise<DefinitionLink[]>;
|
||||||
|
definitionYaml(document: TextDocument, position: Position): Promise<DefinitionLink[] | undefined>;
|
||||||
|
codeActionsJson(document: TextDocument, params: CodeActionParams): Promise<CodeAction[]>;
|
||||||
|
codeActionsYaml(document: TextDocument, params: CodeActionParams): Promise<CodeAction[] | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JsonYamlOptions {
|
||||||
|
resolveWorkspaceUri?(relativePath: string): string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createJsonYamlServices(options: JsonYamlOptions = {}): JsonYamlServices {
|
||||||
|
const schemaFetcher: SchemaFetcher = cacheSchemas((uri) => fetchSchema(uri, options.resolveWorkspaceUri));
|
||||||
|
|
||||||
|
const jsonService = createJsonLanguageService({
|
||||||
|
schemaRequestService: schemaFetcher,
|
||||||
|
workspaceContext: {
|
||||||
|
resolveRelativePath(relativePath, resource) {
|
||||||
|
return resolveRelative(relativePath, resource, options.resolveWorkspaceUri);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
contributions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const yamlService = createYamlLanguageService({
|
||||||
|
schemaRequestService: schemaFetcher,
|
||||||
|
workspaceContext: {
|
||||||
|
resolveRelativePath(relativePath, resource) {
|
||||||
|
return resolveRelative(relativePath, resource, options.resolveWorkspaceUri);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
telemetry: { send: () => {}, sendError: () => {}, sendTrack: () => {} },
|
||||||
|
clientCapabilities: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseYamlSettings: YamlLanguageSettings = {
|
||||||
|
hover: true,
|
||||||
|
completion: true,
|
||||||
|
validate: true,
|
||||||
|
format: true,
|
||||||
|
schemas: [],
|
||||||
|
};
|
||||||
|
yamlService.configure(baseYamlSettings);
|
||||||
|
let defaultSchemaUri = './schema.json';
|
||||||
|
|
||||||
|
return {
|
||||||
|
async validateJson(document) {
|
||||||
|
const jsonDocument = getJsonDocument(jsonService, document);
|
||||||
|
return jsonService.doValidation(document, jsonDocument);
|
||||||
|
},
|
||||||
|
async validateYaml(document) {
|
||||||
|
return yamlService.doValidation(document, false);
|
||||||
|
},
|
||||||
|
async completeJson(document, position) {
|
||||||
|
const jsonDocument = getJsonDocument(jsonService, document);
|
||||||
|
return jsonService.doComplete(document, position, jsonDocument);
|
||||||
|
},
|
||||||
|
async completeYaml(document, position) {
|
||||||
|
return yamlService.doComplete(document, position, false);
|
||||||
|
},
|
||||||
|
async hoverJson(document, position) {
|
||||||
|
const jsonDocument = getJsonDocument(jsonService, document);
|
||||||
|
return jsonService.doHover(document, position, jsonDocument);
|
||||||
|
},
|
||||||
|
async hoverYaml(document, position) {
|
||||||
|
return yamlService.doHover(document, position);
|
||||||
|
},
|
||||||
|
configure(settings = {}) {
|
||||||
|
if (settings.json) {
|
||||||
|
const { defaultSchemaUri: schemaUri, ...jsonLanguageSettings } = settings.json;
|
||||||
|
if (schemaUri) {
|
||||||
|
defaultSchemaUri = schemaUri;
|
||||||
|
}
|
||||||
|
jsonService.configure(jsonLanguageSettings);
|
||||||
|
}
|
||||||
|
yamlService.configure({
|
||||||
|
...baseYamlSettings,
|
||||||
|
...settings.yaml,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async definitionJson(document, position) {
|
||||||
|
const jsonDocument = getJsonDocument(jsonService, document);
|
||||||
|
return jsonService.findDefinition(document, position, jsonDocument);
|
||||||
|
},
|
||||||
|
async definitionYaml(document, position) {
|
||||||
|
return yamlService.doDefinition(document, {
|
||||||
|
textDocument: { uri: document.uri },
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async codeActionsJson(document, params) {
|
||||||
|
if (params.context.only && !params.context.only.includes(CodeActionKind.QuickFix)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return buildSchemaQuickFix(document, defaultSchemaUri);
|
||||||
|
},
|
||||||
|
async codeActionsYaml(document, params) {
|
||||||
|
return yamlService.getCodeAction(document, params) ?? [];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheSchemas(fetcher: SchemaFetcher): SchemaFetcher {
|
||||||
|
const cache = new Map<string, string>();
|
||||||
|
return async (uri: string) => {
|
||||||
|
if (cache.has(uri)) return cache.get(uri)!;
|
||||||
|
const result = await fetcher(uri);
|
||||||
|
cache.set(uri, result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchSchema(uri: string, resolveWorkspaceUri?: (relativePath: string) => string | undefined): Promise<string> {
|
||||||
|
const normalized = normalizeUri(uri, resolveWorkspaceUri);
|
||||||
|
if (normalized.startsWith('file://')) {
|
||||||
|
const filePath = fileURLToPath(normalized);
|
||||||
|
return readFile(filePath, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(normalized);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch schema ${normalized}: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJsonDocument(service: ReturnType<typeof createJsonLanguageService>, document: TextDocument): JSONDocument {
|
||||||
|
return service.parseJSONDocument(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSchemaQuickFix(document: TextDocument, schemaUri: string): CodeAction[] {
|
||||||
|
const content = document.getText();
|
||||||
|
const trimmed = content.trimStart();
|
||||||
|
if (!trimmed.startsWith('{') || content.includes('"$schema"')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const firstBrace = content.indexOf('{');
|
||||||
|
if (firstBrace === -1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const insertOffset = firstBrace + 1;
|
||||||
|
const postText = content.slice(insertOffset);
|
||||||
|
const nextToken = postText.match(/[^\s]/)?.[0] ?? '}';
|
||||||
|
const needsComma = nextToken !== '}';
|
||||||
|
const insertText = `\n "$schema": "${schemaUri}"${needsComma ? ',' : ''}`;
|
||||||
|
const position = document.positionAt(insertOffset);
|
||||||
|
const range = { start: position, end: position };
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Add $schema reference',
|
||||||
|
kind: CodeActionKind.QuickFix,
|
||||||
|
edit: {
|
||||||
|
changes: {
|
||||||
|
[document.uri]: [{ range, newText: insertText }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRelative(
|
||||||
|
relativePath: string,
|
||||||
|
resource: string,
|
||||||
|
resolveWorkspaceUri?: (relativePath: string) => string | undefined,
|
||||||
|
): string {
|
||||||
|
try {
|
||||||
|
const base = new URL(resource);
|
||||||
|
return new URL(relativePath, base).toString();
|
||||||
|
} catch {
|
||||||
|
return resolveWorkspaceUri?.(relativePath) ?? relativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeUri(uri: string, resolveWorkspaceUri?: (relativePath: string) => string | undefined): string {
|
||||||
|
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(uri)) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
const resolved = resolveWorkspaceUri?.(uri);
|
||||||
|
if (resolved) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
return uri.startsWith('/') ? `file://${uri}` : uri;
|
||||||
|
}
|
||||||
250
examples/json-yaml-lsp/src/server.ts
Normal file
250
examples/json-yaml-lsp/src/server.ts
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import type {
|
||||||
|
CodeAction,
|
||||||
|
CodeActionParams,
|
||||||
|
CompletionList,
|
||||||
|
DefinitionLink,
|
||||||
|
Diagnostic,
|
||||||
|
Hover,
|
||||||
|
InitializeParams,
|
||||||
|
InitializeResult,
|
||||||
|
WorkspaceDocumentDiagnosticReport,
|
||||||
|
} from 'vscode-languageserver/node';
|
||||||
|
import { CodeActionKind, DocumentDiagnosticReportKind } from 'vscode-languageserver/node';
|
||||||
|
import { createConnection, createServer } from '@volar/language-server/node';
|
||||||
|
import { createSimpleProject } from '@volar/language-server/lib/project/simpleProject';
|
||||||
|
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
import { URI, Utils } from 'vscode-uri';
|
||||||
|
import { createJsonYamlServices, type JsonYamlConfiguration } from './jsonYaml.js';
|
||||||
|
|
||||||
|
const connection = createConnection();
|
||||||
|
const server = createServer(connection);
|
||||||
|
const project = createSimpleProject([]);
|
||||||
|
const languageServicePlugins: [] = [];
|
||||||
|
|
||||||
|
const structuredServices = createJsonYamlServices({
|
||||||
|
resolveWorkspaceUri: (relativePath) => {
|
||||||
|
const [root] = server.workspaceFolders.all;
|
||||||
|
if (!root) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return Utils.joinPath(root, relativePath).toString();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const pendingValidation = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
const VALIDATION_DELAY = 200;
|
||||||
|
|
||||||
|
server.onInitialize((capabilities) => {
|
||||||
|
capabilities.completionProvider = {
|
||||||
|
triggerCharacters: ['.', '"', '/', '<', ':'],
|
||||||
|
};
|
||||||
|
capabilities.hoverProvider = true;
|
||||||
|
capabilities.definitionProvider = true;
|
||||||
|
capabilities.codeActionProvider = {
|
||||||
|
codeActionKinds: [CodeActionKind.QuickFix],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||||
|
return server.initialize(params, project, languageServicePlugins);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onInitialized(async () => {
|
||||||
|
server.initialized();
|
||||||
|
await applyConfiguration();
|
||||||
|
server.configurations.onDidChange(applyConfiguration);
|
||||||
|
server.documents.all().forEach((document) => queueValidation(document.uri));
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onShutdown(() => {
|
||||||
|
server.shutdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.documents.onDidOpen(({ document }) => {
|
||||||
|
queueValidation(document.uri);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.documents.onDidChangeContent(({ document }) => {
|
||||||
|
queueValidation(document.uri);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.documents.onDidClose(({ document }) => {
|
||||||
|
cancelQueuedValidation(document.uri);
|
||||||
|
connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onCompletion(async (params, token): Promise<CompletionList | null> => {
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = getDocument(params.textDocument.uri);
|
||||||
|
if (!document) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structuredServices.completeJson(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structuredServices.completeYaml(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onHover(async (params, token): Promise<Hover | null> => {
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = getDocument(params.textDocument.uri);
|
||||||
|
if (!document) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structuredServices.hoverJson(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structuredServices.hoverYaml(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onDefinition(async (params, token): Promise<DefinitionLink[] | null> => {
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const document = getDocument(params.textDocument.uri);
|
||||||
|
if (!document) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structuredServices.definitionJson(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structuredServices.definitionYaml(document, params.position);
|
||||||
|
return token.isCancellationRequested ? null : result ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onCodeAction(async (params, token): Promise<CodeAction[] | null> => {
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const document = getDocument(params.textDocument.uri);
|
||||||
|
if (!document) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
const result = await structuredServices.codeActionsJson(document, params);
|
||||||
|
return token.isCancellationRequested ? null : result;
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
const result = await structuredServices.codeActionsYaml(document, params);
|
||||||
|
return token.isCancellationRequested ? null : result ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.languages.diagnostics.onWorkspace(async (_params, token) => {
|
||||||
|
const items: WorkspaceDocumentDiagnosticReport[] = [];
|
||||||
|
for (const doc of server.documents.all()) {
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
uri: doc.uri,
|
||||||
|
kind: DocumentDiagnosticReportKind.Full,
|
||||||
|
version: doc.version ?? null,
|
||||||
|
items: await collectDiagnostics(doc),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { items };
|
||||||
|
});
|
||||||
|
|
||||||
|
async function validateDocument(document: TextDocument) {
|
||||||
|
const diagnostics = await collectDiagnostics(document);
|
||||||
|
|
||||||
|
const latest = getDocument(document.uri);
|
||||||
|
if (!latest || latest.version !== document.version) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.sendDiagnostics({
|
||||||
|
uri: document.uri,
|
||||||
|
diagnostics,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueValidation(uri: string) {
|
||||||
|
cancelQueuedValidation(uri);
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
pendingValidation.delete(uri);
|
||||||
|
const document = getDocument(uri);
|
||||||
|
if (document) {
|
||||||
|
validateDocument(document);
|
||||||
|
}
|
||||||
|
}, VALIDATION_DELAY);
|
||||||
|
pendingValidation.set(uri, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelQueuedValidation(uri: string) {
|
||||||
|
const handle = pendingValidation.get(uri);
|
||||||
|
if (handle) {
|
||||||
|
clearTimeout(handle);
|
||||||
|
pendingValidation.delete(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectDiagnostics(document: TextDocument) {
|
||||||
|
const backend = pickBackend(document);
|
||||||
|
if (backend === 'json') {
|
||||||
|
return structuredServices.validateJson(document);
|
||||||
|
}
|
||||||
|
if (backend === 'yaml') {
|
||||||
|
return structuredServices.validateYaml(document);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyConfiguration() {
|
||||||
|
const config =
|
||||||
|
(await server.configurations.get<JsonYamlConfiguration>('volarJsonYaml', undefined)) ?? {};
|
||||||
|
structuredServices.configure(config);
|
||||||
|
await server.languageFeatures.requestRefresh(false);
|
||||||
|
server.documents.all().forEach((document) => queueValidation(document.uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
type Backend = 'json' | 'yaml' | 'other';
|
||||||
|
|
||||||
|
function pickBackend(document: TextDocument): Backend {
|
||||||
|
if (document.languageId === 'json' || document.uri.endsWith('.json')) {
|
||||||
|
return 'json';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
document.languageId === 'yaml' ||
|
||||||
|
document.uri.endsWith('.yaml') ||
|
||||||
|
document.uri.endsWith('.yml')
|
||||||
|
) {
|
||||||
|
return 'yaml';
|
||||||
|
}
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocument(uri: string): TextDocument | undefined {
|
||||||
|
return server.documents.get(URI.parse(uri)) as TextDocument | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.listen();
|
||||||
14
examples/json-yaml-lsp/tsconfig.json
Normal file
14
examples/json-yaml-lsp/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ES2020",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"resolveJsonModule": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user