Парсер, используемый в экосистеме ESLint для Vue, представляет собой специализированный инструмент, который расширяет стандартные возможности анализа JavaScript-кода за счёт понимания Single File Components (SFC). Основная задача такого парсера заключается в построении единого абстрактного синтаксического дерева (AST), включающего одновременно JavaScript, шаблоны Vue и встроенные выражения, что позволяет линтерам работать с кодом Vue на уровне, недоступном обычному парсеру ECMAScript.
В экосистеме Vue ключевую роль играет пакет
vue-eslint-parser, который выступает прослойкой между
ESLint и реальным содержимым .vue файлов. Его архитектура
построена таким образом, чтобы разделять разные языковые контексты
внутри одного файла и корректно объединять результаты анализа.
Vue Single File Component состоит из трёх основных секций:
<template> — декларативный шаблон<script> — логика компонента на
JavaScript/TypeScript<style> — стили (не участвуют в ESLint-анализе
напрямую)Каждая секция имеет собственную семантику, синтаксис и правила
интерпретации. Стандартный парсер ESLint (например, espree)
не способен корректно обработать <template>, поэтому
используется vue-eslint-parser, который выполняет
предварительное разбиение файла на части.
Внутренний процесс можно представить как последовательность этапов:
.vue файла на блоки (template, script, script
setup)<template> через HTML-подобный парсер
Vue<script> через указанный
JavaScript/TypeScript парсерAST, формируемый vue-eslint-parser, отличается от
стандартного ESTree. Он является гибридным и включает:
Ключевая особенность заключается в том, что шаблон Vue не компилируется напрямую в JavaScript на этапе ESLint. Вместо этого создаётся отдельное дерево представления шаблона.
Пример структуры:
VDocumentFragment
VElement (div)
VElement (span)
Каждый VExpressionContainer содержит
JavaScript-выражение, которое может быть дополнительно распарсено
стандартным JS-парсером.
<template> и выражений VueШаблон Vue использует собственный синтаксис директив:
v-ifv-forv-bindv-on:prop, @eventПарсер не преобразует их в JavaScript напрямую. Вместо этого каждая директива становится отдельным узлом AST.
Например:
<template>
<div v-if="isVisible">
{{ message }}
</div>
</template>
После парсинга формируется структура:
Element: div
directive: v-if
interpolation:
Важно, что выражения внутри {{ }} и атрибутов директив
обрабатываются отдельным JavaScript-парсером, указанным в конфигурации
ESLint.
vue-eslint-parser не заменяет полностью
JavaScript-парсер, а делегирует обработку <script> и
выражений шаблона внешнему парсеру.
Конфигурация задаётся через parserOptions.parser:
module.exports = {
parser: "vue-eslint-parser",
parserOptions: {
parser: "@babel/eslint-parser"
}
};
Также могут использоваться:
@typescript-eslint/parser для TypeScriptespree (по умолчанию)babel-eslint (устаревший вариант)Таким образом достигается гибкость: Vue-слой обрабатывает структуру SFC, а JavaScript-слой отвечает за семантику кода.
С появлением Composition API и синтаксиса
<script setup> парсер был расширен для поддержки
упрощённого объявления логики компонента.
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
Особенность заключается в том, что script setup не имеет
явного export default. Парсер виртуально трансформирует его
в компонентную структуру, чтобы ESLint-правила могли анализировать:
При этом создаётся виртуальный контекст компонента, который связывает
script setup и <template>.
Одной из ключевых задач парсера является установление связей между шаблоном и логикой компонента.
Пример:
<template>
<button @click="increment">{{ count }}</button>
</template>
<script>
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
</script>
Парсер формирует связь:
count в template → data().countincrement → methods.incrementЭто позволяет ESLint-правилам выполнять семантический анализ использования переменных, например:
Vue использует концепцию виртуального DOM (VNode). Хотя ESLint не работает напрямую с VNode, парсер формирует промежуточную модель, близкую к ней.
Каждый элемент шаблона представлен как:
Эта структура позволяет реализовывать правила линтинга, специфичные для Vue:
v-if и v-for на одном
элементеПарсер vue-eslint-parser практически всегда используется
совместно с eslint-plugin-vue, который предоставляет набор
правил для Vue SFC.
Связка выглядит следующим образом:
vue-eslint-parserПримеры правил:
vue/no-unused-varsvue/no-mutating-propsvue/require-default-propvue/valid-v-forКаждое правило опирается на специфичную структуру AST, недоступную в стандартном ESLint.
При использовании TypeScript внутри Vue файлов парсер становится многоуровневым:
Пример конфигурации:
parserOptions: {
parser: "@typescript-eslint/parser",
extraFileExtensions: [".vue"]
}
Это позволяет анализировать:
Несмотря на расширенные возможности, парсер имеет ряд ограничений:
Особенно сложными являются случаи:
<component :is="...">Выражения внутри template обрабатываются отдельно и имеют собственный контекст.
<div>{{ user.name.toUpperCase() }}</div>
Парсер:
user.name.toUpperCase()При этом отсутствует доступ к полному TypeScript-типу, если не используется специализированная интеграция.
При некорректном синтаксисе .vue файла парсер возвращает
структурированные ошибки:
Каждая ошибка содержит:
Это позволяет ESLint точно указывать источник проблемы даже в сложных SFC.
vue-eslint-parser предоставляет дополнительные сервисы
через context.parserServices:
Пример использования:
Эти сервисы являются основой для создания правил уровня framework-aware линтинга.
Парсинг Vue файлов значительно сложнее стандартного JS из-за двойной структуры. Оптимизация достигается за счёт:
На больших проектах основная нагрузка приходится именно на template parsing и связывание контекстов.
Архитектура позволяет расширять поведение через:
Это делает возможным поддержку:
Некоторые проекты используют JSX вместо template:
export default {
render() {
return <div>{this.message}</div>
}
}
В этом случае vue-eslint-parser передаёт обработку JSX в
JavaScript-парсер, а Vue-специфичная логика минимизируется.
Однако смешивание JSX и template в одном проекте требует аккуратной настройки parserOptions.