5.0 KiB
CLI Integration & Headless Workflows
Docs Index • Repo README • Workspace Diagnostics • Testing & CI • LSP Benchmarking
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):
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
#!/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:
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:
if (argv.json) {
console.log(JSON.stringify({ uri, diagnostics }, null, 2));
}
Leveraging workspace/diagnostic
Instead of looping per file, reuse the workspace diagnostics pipeline:
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
- Add a script (
volar-check) that runs your CLI. - Wire it into CI workflows:
- run: npm run volar-check -- --json > artifacts/volar.json
- uses: actions/upload-artifact@v4
with:
name: volar-diagnostics
path: artifacts/volar.json
- Fail the pipeline when exit code ≠ 0.
Pre-commit Hooks
Use lint-staged or husky to run targeted diagnostics:
{
"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:
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/nexeif users lack Node. - Provide
npxentry 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.