Парсер для Vue

Парсер, используемый в экосистеме 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, который выполняет предварительное разбиение файла на части.

Внутренний процесс можно представить как последовательность этапов:

  1. Разбор .vue файла на блоки (template, script, script setup)
  2. Парсинг <template> через HTML-подобный парсер Vue
  3. Парсинг <script> через указанный JavaScript/TypeScript парсер
  4. Объединение AST в единую структуру с сохранением координат исходного кода
  5. Обогащение узлов дополнительной информацией для ESLint-правил

AST Vue: гибридная модель

AST, формируемый vue-eslint-parser, отличается от стандартного ESTree. Он является гибридным и включает:

  • узлы JavaScript AST (ExpressionStatement, FunctionDeclaration и т.д.)
  • узлы шаблона Vue (VElement, VAttribute, VExpressionContainer)
  • синтетические узлы для связывания template и script

Ключевая особенность заключается в том, что шаблон Vue не компилируется напрямую в JavaScript на этапе ESLint. Вместо этого создаётся отдельное дерево представления шаблона.

Пример структуры:

  • VDocumentFragment

    • VElement (div)

      • VAttribute (v-if)
      • VExpressionContainer (message)
    • VElement (span)

Каждый VExpressionContainer содержит JavaScript-выражение, которое может быть дополнительно распарсено стандартным JS-парсером.


Обработка <template> и выражений Vue

Шаблон Vue использует собственный синтаксис директив:

  • v-if
  • v-for
  • v-bind
  • v-on
  • сокращённые формы :prop, @event

Парсер не преобразует их в JavaScript напрямую. Вместо этого каждая директива становится отдельным узлом AST.

Например:

<template>
  <div v-if="isVisible">
    {{ message }}
  </div>
</template>

После парсинга формируется структура:

  • Element: div

    • directive: v-if

      • expression: isVisible
    • interpolation:

      • expression: message

Важно, что выражения внутри {{ }} и атрибутов директив обрабатываются отдельным JavaScript-парсером, указанным в конфигурации ESLint.


parserOptions и выбор JavaScript-парсера

vue-eslint-parser не заменяет полностью JavaScript-парсер, а делегирует обработку <script> и выражений шаблона внешнему парсеру.

Конфигурация задаётся через parserOptions.parser:

module.exports = {
  parser: "vue-eslint-parser",
  parserOptions: {
    parser: "@babel/eslint-parser"
  }
};

Также могут использоваться:

  • @typescript-eslint/parser для TypeScript
  • espree (по умолчанию)
  • babel-eslint (устаревший вариант)

Таким образом достигается гибкость: Vue-слой обрабатывает структуру SFC, а JavaScript-слой отвечает за семантику кода.


Script setup и особенности разбора

С появлением Composition API и синтаксиса <script setup> парсер был расширен для поддержки упрощённого объявления логики компонента.

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

Особенность заключается в том, что script setup не имеет явного export default. Парсер виртуально трансформирует его в компонентную структуру, чтобы ESLint-правила могли анализировать:

  • объявленные переменные
  • импорты
  • реактивные состояния
  • использование в template

При этом создаётся виртуальный контекст компонента, который связывает script setup и <template>.


Связывание template и script

Одной из ключевых задач парсера является установление связей между шаблоном и логикой компонента.

Пример:

<template>
  <button @click="increment">{{ count }}</button>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

Парсер формирует связь:

  • count в template → data().count
  • incrementmethods.increment

Это позволяет ESLint-правилам выполнять семантический анализ использования переменных, например:

  • проверка неиспользуемых переменных
  • поиск неопределённых идентификаторов
  • контроль корректности методов

VNode-модель и её роль в анализе

Vue использует концепцию виртуального DOM (VNode). Хотя ESLint не работает напрямую с VNode, парсер формирует промежуточную модель, близкую к ней.

Каждый элемент шаблона представлен как:

  • тип узла
  • атрибуты
  • директивы
  • дочерние элементы
  • выражения

Эта структура позволяет реализовывать правила линтинга, специфичные для Vue:

  • запрет использования v-if и v-for на одном элементе
  • проверка корректности key в списках
  • анализ доступности переменных в шаблоне

Интеграция с eslint-plugin-vue

Парсер vue-eslint-parser практически всегда используется совместно с eslint-plugin-vue, который предоставляет набор правил для Vue SFC.

Связка выглядит следующим образом:

  • ESLint вызывает vue-eslint-parser
  • Парсер формирует AST
  • eslint-plugin-vue применяет правила к AST

Примеры правил:

  • vue/no-unused-vars
  • vue/no-mutating-props
  • vue/require-default-prop
  • vue/valid-v-for

Каждое правило опирается на специфичную структуру AST, недоступную в стандартном ESLint.


Обработка TypeScript в Vue

При использовании TypeScript внутри Vue файлов парсер становится многоуровневым:

  • vue-eslint-parser обрабатывает SFC
  • @typescript-eslint/parser обрабатывает script блок

Пример конфигурации:

parserOptions: {
  parser: "@typescript-eslint/parser",
  extraFileExtensions: [".vue"]
}

Это позволяет анализировать:

  • типы props
  • интерфейсы компонентов
  • generics в Composition API
  • типизацию emit событий

Ограничения и особенности анализа

Несмотря на расширенные возможности, парсер имеет ряд ограничений:

  • невозможность полной эмуляции runtime Vue
  • частичная точность связывания template/script в сложных случаях
  • ограниченный анализ динамических выражений
  • различия между compile-time и runtime логикой

Особенно сложными являются случаи:

  • динамические компоненты <component :is="...">
  • вычисляемые имена свойств
  • сложные цепочки реактивности

Работа с выражениями в шаблоне

Выражения внутри template обрабатываются отдельно и имеют собственный контекст.

<div>{{ user.name.toUpperCase() }}</div>

Парсер:

  • выделяет выражение user.name.toUpperCase()
  • передаёт его в JS-парсер
  • связывает результат с контекстом шаблона

При этом отсутствует доступ к полному TypeScript-типу, если не используется специализированная интеграция.


Ошибки парсинга и диагностика

При некорректном синтаксисе .vue файла парсер возвращает структурированные ошибки:

  • ошибки HTML-подобного синтаксиса
  • ошибки JavaScript внутри script
  • ошибки выражений Vue

Каждая ошибка содержит:

  • позицию в исходном файле
  • тип блока (template/script)
  • описание проблемы
  • фрагмент AST

Это позволяет ESLint точно указывать источник проблемы даже в сложных SFC.


Внутренние сервисы parser services

vue-eslint-parser предоставляет дополнительные сервисы через context.parserServices:

  • доступ к template AST
  • доступ к template body tokens
  • связывание переменных template ↔︎ script
  • utilities для написания кастомных правил

Пример использования:

  • получение VNode узлов
  • анализ выражений в директивах
  • сопоставление идентификаторов

Эти сервисы являются основой для создания правил уровня framework-aware линтинга.


Производительность и оптимизация

Парсинг Vue файлов значительно сложнее стандартного JS из-за двойной структуры. Оптимизация достигается за счёт:

  • кэширования результатов парсинга script блока
  • ленивого разбора template выражений
  • минимизации повторного обхода AST
  • разделения ответственности между парсерами

На больших проектах основная нагрузка приходится именно на template parsing и связывание контекстов.


Расширяемость парсера

Архитектура позволяет расширять поведение через:

  • кастомные JS-парсеры
  • дополнительные parser services
  • сторонние ESLint правила
  • обёртки над AST

Это делает возможным поддержку:

  • Vue 2 и Vue 3 одновременно
  • JSX внутри Vue компонентов
  • гибридных проектов (Vue + TS + JSX)

Особенности работы с JSX в Vue

Некоторые проекты используют JSX вместо template:

export default {
  render() {
    return <div>{this.message}</div>
  }
}

В этом случае vue-eslint-parser передаёт обработку JSX в JavaScript-парсер, а Vue-специфичная логика минимизируется.

Однако смешивание JSX и template в одном проекте требует аккуратной настройки parserOptions.