# 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.