# How TSSLint Handles Virtual TypeScript Files for Vue, MDX, and Astro

> Discover how TSSLint processes virtual TypeScript files for Vue, MDX, and Astro by leveraging language plugins and mapping diagnostics back to original sources.

- Repository: [Johnson Chu/tsslint](https://github.com/johnsoncodehk/tsslint)
- Tags: internals
- Published: 2026-03-04

---

**TSSLint treats Vue, MDX, and Astro files as virtual TypeScript files by loading framework-specific language plugins from @volar/language-hub, decorating the TypeScript service host to expose virtual file names, and mapping diagnostics back to the original source files.**

TSSLint provides first-class linting support for modern meta-frameworks by converting non-TypeScript source files into virtual TypeScript representations. This architecture enables type-aware lint rules to run against Vue Single File Components (SFCs), MDX documents, and Astro templates as if they were standard `.ts` files.

## Language Plugin Discovery and Loading

The CLI entry point orchestrates framework support through the `load()` function in [`packages/cli/lib/languagePlugins.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/languagePlugins.ts). This module dynamically instantiates language plugins based on command-line flags.

### CLI Flags for Meta-Frameworks

TSSLint exposes dedicated flags for each supported framework:

- `--vue-project` – Enables Vue SFC support via `createVuePlugin`
- `--vue-vine-project` – Enables Vue Vine support via `createVueVinePlugins`
- `--mdx-project` – Enables MDX support via `createMdxPlugin`
- `--astro-project` – Enables Astro support via `createAstroPlugin`

### Volar Language Hub Integration

Each flag triggers the corresponding factory function from **@volar/language-hub**. For example, when `--vue-project` is specified, the CLI executes:

```typescript
createVuePlugin(ts, tsconfig)

```

These plugins implement the Volar language plugin API, which defines how to parse framework-specific syntax and generate virtual TypeScript files containing the extracted script content plus type declarations for template bindings.

## Decorating the TypeScript Service Host

Once plugins are loaded, TSSLint integrates them into the TypeScript language service through host decoration in [`packages/cli/lib/worker.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/worker.ts).

### Virtual File Name Exposure

At line 189 in [`worker.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/worker.ts), the CLI calls:

```typescript
decorateLanguageServiceHost(ts, language, linterHost)

```

This function from **@volar/typescript** augments the `LanguageServiceHost` with three critical capabilities:

1. **Virtual file enumeration** – Exposes generated `.ts` file names for each `.vue`, `.mdx`, or `.astro` source
2. **Snapshot provision** – Implements `getScriptSnapshot` to return `ts.IScriptSnapshot` instances containing the virtual TypeScript content
3. **Extension registration** – Adds framework extensions to `typescript.extraFileExtensions`

### Enabling Non-TypeScript Extensions

To prevent the TypeScript compiler from rejecting non-standard extensions, [`worker.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/worker.ts) lines 103-108 check if any plugin contributes to `extraFileExtensions`. If so, the CLI sets:

```typescript
allowNonTsExtensions: true

```

This configuration allows the language service to process `.vue`, `.mdx`, and `.astro` files without throwing extension-related errors.

## Linting Virtual Files with the Core Engine

The core linting engine in [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) operates transparently on both physical and virtual files. After host decoration, the linter is instantiated at lines 58-74:

```typescript
linter = core.createLinter(
    { languageService: linterLanguageService,
      languageServiceHost: linterHost,
      typescript: ts },
    path.dirname(configFile),
    config,
    () => [],
    linterSyntaxOnlyLanguageService,
);

```

The `languageService` now recognizes virtual TypeScript files as standard source files. When `linter.lint(fileName, cache)` executes, it processes the virtual `.ts` content generated from Vue templates, MDX components, or Astro frontmatter, enabling type-aware rules to validate framework-specific syntax.

## Mapping Diagnostics to Source Files

When diagnostics originate from virtual files, TSSLint ensures users see errors mapped to their original source locations. In [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) lines 190-227, the `createRelatedInformation` function rewrites the `file` property of diagnostics:

```typescript
// Simplified representation of the mapping logic
if (diagnostic.file && isVirtualFile(diagnostic.file.fileName)) {
    const sourceFile = getSourceFile(diagnostic.file.fileName);
    diagnostic.file = sourceFile;
}

```

This transformation ensures that error reports point to `.vue`, `.mdx`, or `.astro` files rather than internal virtual `.ts` shadows, providing a seamless developer experience.

## Configuration and Usage Example

To lint Vue SFCs, MDX documents, or Astro templates, specify the framework flag when running the CLI:

```bash

# Lint Vue files

tsslint --vue-project ./tsconfig.json

# Lint MDX files

tsslint --mdx-project ./tsconfig.json

# Lint Astro files

tsslint --astro-project ./tsconfig.json

```

Configure rule inclusion in [`tsslint.config.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/tsslint.config.ts):

```typescript
// tsslint.config.ts
export default {
  include: ['**/*.vue', '**/*.mdx', '**/*.astro', '**/*.ts'],
  rules: {
    'no-console': 'error',
    'no-unused-vars': 'error',
  },
};

```

## Summary

- **TSSLint handles Vue, MDX, and Astro files as virtual TypeScript files** through the Volar language plugin system.
- **Framework plugins are loaded dynamically** via [`packages/cli/lib/languagePlugins.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/languagePlugins.ts) based on CLI flags like `--vue-project` and `--mdx-project`.
- **The TypeScript service host is decorated** in [`packages/cli/lib/worker.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/worker.ts) to expose virtual file names and enable non-TypeScript extensions.
- **The core linter operates transparently** on virtual files in [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts), treating generated TypeScript from framework files as standard source.
- **Diagnostics are mapped back to original files** so developers see errors in their `.vue`, `.mdx`, or `.astro` sources rather than virtual shadows.

## Frequently Asked Questions

### How does TSSLint differ from ESLint when linting Vue files?

TSSLint uses the TypeScript language service to create virtual TypeScript representations of Vue SFCs, enabling type-aware linting against actual TypeScript types extracted from templates. ESLint typically uses parsers like `vue-eslint-parser` to generate an AST, but does not inherently provide TypeScript type information for template bindings without additional configuration.

### Can I use TSSLint with multiple meta-frameworks in the same project?

Yes. You can specify multiple framework flags simultaneously when running the CLI, such as `tsslint --vue-project --mdx-project ./tsconfig.json`. The `load()` function in [`packages/cli/lib/languagePlugins.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/languagePlugins.ts) aggregates all requested plugins into the `languages` array, and the decorated language service host handles virtual files from all frameworks concurrently.

### What happens if a virtual file contains TypeScript errors in the template section?

When a Vue template contains type errors, the `createVuePlugin` generates a virtual TypeScript file that includes type declarations for template bindings. If these declarations conflict with actual types, the TypeScript language service reports diagnostics. The core linter in [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) then maps these diagnostics back to the original `.vue` file using the `createRelatedInformation` logic, displaying the error at the correct line in the template section.

### Does TSSLint support custom language plugins beyond Vue, MDX, and Astro?

Yes. While TSSLint provides built-in CLI flags for Vue, Vue-Vine, MDX, and Astro, the architecture in [`packages/cli/lib/languagePlugins.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/languagePlugins.ts) is designed to accept any plugin conforming to the Volar language plugin API. You can theoretically pass custom language plugin factories to the `load()` function, provided they implement the required methods for generating virtual TypeScript files and providing script snapshots.