eslint-plugin-typed-vue 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # eslint-plugin-typed-vue
2
+
3
+ ESLint plugin that enables typescript-eslint's type-aware rules for Vue SFC files.
4
+
5
+ ## Problem
6
+
7
+ typescript-eslint's type-aware rules (e.g., `@typescript-eslint/no-unsafe-assignment`) don't work correctly with Vue SFCs because TypeScript cannot parse `.vue` files natively. This causes `import Foo from './Foo.vue'` to resolve as `any`, triggering false positives from `no-unsafe-*` rules.
8
+
9
+ ## How It Works
10
+
11
+ This plugin uses [`@vue/language-core`](https://github.com/vuejs/language-tools) to generate virtual TypeScript code from `.vue` files and builds a `ts.Program` that understands `.vue` imports. It then injects this Program into `@typescript-eslint/parser` so type-aware rules work correctly.
12
+
13
+ Additionally, it provides an ESLint Processor that extracts the generated TypeScript code from `.vue` files, enabling typescript-eslint rules to run against template expressions with accurate source position remapping.
14
+
15
+ ```
16
+ .vue file
17
+
18
+ vue-eslint-parser (AST generation)
19
+
20
+ enhanced-parser (@vue/language-core virtual TS → ts.Program)
21
+
22
+ typescript-eslint type-aware rules work correctly
23
+ ```
24
+
25
+ ## Requirements
26
+
27
+ - ESLint >= 9.0.0 (Flat Config)
28
+ - TypeScript >= 5.0.0
29
+ - vue-eslint-parser >= 9.0.0
30
+ - @typescript-eslint/parser >= 8.0.0
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install -D eslint-plugin-typed-vue
36
+ ```
37
+
38
+ Peer dependencies:
39
+
40
+ ```bash
41
+ npm install -D eslint typescript vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ```js
47
+ // eslint.config.js
48
+ import typedVue from "eslint-plugin-typed-vue";
49
+ import tseslint from "typescript-eslint";
50
+
51
+ export default tseslint.config(
52
+ ...tseslint.configs.recommendedTypeChecked,
53
+ ...typedVue.configs.recommended,
54
+ {
55
+ languageOptions: {
56
+ parserOptions: {
57
+ tsconfigRootDir: import.meta.dirname,
58
+ },
59
+ },
60
+ },
61
+ );
62
+ ```
63
+
64
+ The `recommended` config sets up:
65
+
66
+ - **`**/\*.vue`**: `vue-eslint-parser`with`enhanced-parser` as the inner parser, plus the Processor for template type checking
67
+ - **`**/_.ts`, `\*\*/_.tsx`, etc.**: `enhanced-parser`for type-aware linting with`.vue` import support
68
+
69
+ ## Supported Vue SFC Patterns
70
+
71
+ | Pattern | Support |
72
+ | ------------------------------------------- | ------- |
73
+ | `<script lang="ts">` | Yes |
74
+ | `<script setup lang="ts">` | Yes |
75
+ | `<script lang="tsx">` | Yes |
76
+ | `<script>` + `<script setup>` (dual script) | Yes |
77
+ | Cross-component type imports | Yes |
78
+ | `defineProps` / `defineEmits` / generics | Yes |
79
+ | Template expressions (via Processor) | Yes |
80
+
81
+ ## Custom Rules
82
+
83
+ ### `typed-vue/strict-boolean-expressions`
84
+
85
+ Requires boolean expressions in `v-if`, `v-else-if`, and `v-show` directives.
86
+
87
+ ```vue
88
+ <template>
89
+ <!-- Error: Unexpected non-boolean type 'number' -->
90
+ <div v-if="count">...</div>
91
+
92
+ <!-- OK -->
93
+ <div v-if="count > 0">...</div>
94
+ <div v-if="isVisible">...</div>
95
+ </template>
96
+ ```
97
+
98
+ ### `typed-vue/no-unsafe-template-expression`
99
+
100
+ Disallows expressions with `any` type in template interpolations (`{{ }}`) and `v-bind` directives. vue-tsc does not catch these because the generated code accesses the expression without a type constraint.
101
+
102
+ ```vue
103
+ <template>
104
+ <!-- Error: Unsafe use of `any` in interpolation -->
105
+ <div>{{ unsafeData }}</div>
106
+
107
+ <!-- Error: Unsafe use of `any` in v-bind -->
108
+ <input :value="unsafeData" />
109
+
110
+ <!-- OK -->
111
+ <div>{{ typedData }}</div>
112
+ </template>
113
+ ```
114
+
115
+ ### `typed-vue/no-unsafe-event-handler`
116
+
117
+ Disallows expressions with `any` type in event handler directives (`@click`, `v-on:click`, etc.). vue-tsc does not catch these because `any` is assignable to any handler type.
118
+
119
+ ```vue
120
+ <template>
121
+ <!-- Error: Unsafe use of `any` as event handler -->
122
+ <button @click="unsafeHandler">Click</button>
123
+
124
+ <!-- OK -->
125
+ <button @click="typedHandler">Click</button>
126
+ </template>
127
+ ```
128
+
129
+ ## API
130
+
131
+ ### `resetCache()`
132
+
133
+ Resets all internal caches (Program, VueVirtualFiles, tsconfig). Call this when the file system has changed between lint runs (e.g., in watch mode or benchmarks).
134
+
135
+ ```ts
136
+ import { resetCache } from "eslint-plugin-typed-vue";
137
+
138
+ resetCache();
139
+ ```
140
+
141
+ ## Known Limitations
142
+
143
+ ### Performance scales with file count
144
+
145
+ The plugin uses `ts.createProgram` with incremental compilation (`oldProgram`). Each `.vue` file requires a per-file Program rebuild because the source code provided by `vue-eslint-parser` may differ from the on-disk content. This results in approximately O(N) scaling per file as the number of files grows. For very large projects (100+ Vue files), lint times may become noticeable.
146
+
147
+ ### No autofix support in Processor
148
+
149
+ The ESLint Processor's `supportsAutofix` is set to `false`. Errors detected in template expressions via the Processor cannot be auto-fixed because the source mapping between generated TypeScript code and the original `.vue` template is not bidirectional for fix ranges.
150
+
151
+ ### `@vue/language-core` version coupling
152
+
153
+ The plugin depends on `@vue/language-core` v3.x (and `@volar/language-core` v2.x for type definitions). Changes to the virtual file format in these packages may require updates to this plugin.
154
+
155
+ ### Template-only errors from Processor
156
+
157
+ The Processor filters typescript-eslint errors to the `<template>` region only. Errors in `<script>` blocks are handled by the standard parser path (block 0) and are not duplicated.
158
+
159
+ ### `projectService` is not supported
160
+
161
+ This plugin uses `parserOptions.programs` to inject a custom TypeScript Program that understands `.vue` files via `@vue/language-core`. This is incompatible with typescript-eslint's `projectService` option, which uses TypeScript's native Project Service APIs.
162
+
163
+ If your ESLint config has `projectService: true`, this plugin will override it for `.vue` and `.ts` files. This is intentional — `projectService` cannot perform the `.vue` virtual file transformation that this plugin provides.
164
+
165
+ The `programs` option is [not deprecated](https://typescript-eslint.io/blog/project-service/) and will remain available until `projectService` can fully replace it.
166
+
167
+ ### `tsconfig.json` must include `.vue` files
168
+
169
+ Your `tsconfig.json` (or `tsconfig.app.json`) must be configured to include `.vue` files. Typically this is done via `vue-tsc` or by adding `"include": ["src/**/*.vue", "src/**/*.ts"]`.
170
+
171
+ ## License
172
+
173
+ MIT