@eventra_dev/cli-plugin-vue

Eventra CLI plugin — extract track() calls from Vue SFC (.vue) files
1,736
Install
npm install @eventra_dev/cli-plugin-vue
Latest Version:1.1.1
License:MIT
Last Updated:Oct 17, 2026
Links: GitHub  ·  npm
Maintainer: eventra_dev

Eventra CLI Plugin — Vue

npm version npm downloads tests passing coverage node TypeScript license

Official Eventra CLI plugin — extracts track() calls from Vue Single-File Components (.vue), so eventra sync/check/watch understand Vue and Nuxt code the same way they already understand plain TypeScript.


Overview

The CLI core is framework-agnostic and only walks .ts/.tsx/.js/.jsx. This plugin teaches it .vue: it parses each SFC with the real Vue compiler (@vue/compiler-sfc) — not a regex — and hands the CLI a single virtual TypeScript module per file, so every existing detection rule (direct SDK calls, function wrappers, cross-file propagation, dynamic-name reporting) applies to Vue code without any Vue-specific case in the core engine.

Vue and Nuxt .vue files are handled the same way — Nuxt pages/layouts/components are ordinary Vue SFCs. See Nuxt below for auto-import composables.


Installation

npm install -D @eventra_dev/cli-plugin-vue @eventra_dev/eventra-cli
# or
pnpm add -D @eventra_dev/cli-plugin-vue @eventra_dev/eventra-cli

Enable it in eventra.json:

{
  "plugins": ["@eventra_dev/cli-plugin-vue"],
  "sync": {
    "include": ["**/*.{ts,tsx,js,jsx}"],
    "exclude": ["node_modules", "dist", ".next", ".git"]
  }
}

sync.include does not need **/*.vue added manually — the plugin registers it via includeGlobs.


What gets detected

<script> / <script setup>

Handled exactly like a regular .ts file — direct SDK calls, function wrappers, variables, ternaries, cross-file propagation all apply:

<script setup lang="ts">
import { Eventra } from "@eventra_dev/eventra-sdk";

const tracker = new Eventra({ apiKey: "YOUR_PROJECT_API_KEY" });

tracker.track("checkout.started");
</script>

<script> + <script setup> in the same file are merged in source order. defineProps/defineEmits/defineExpose/defineOptions/withDefaults pass through untouched.

<template> — literal event attributes

<template>
  <Button event="checkout.cta" />
</template>

<template> — dynamic event attributes

<template>
  <Button :event="computedEventName" />
</template>

The expression is copied as-is into the same module scope as the script. If it resolves to a real script-level constant, the event name is detected normally; otherwise it's reported as a dynamic occurrence (same mechanism as tracker.track(someVariable) in plain TypeScript) instead of being silently dropped.

External blocks

<script src="./logic.ts"> and <template src="./view.html"> are resolved and read from disk.


<template> - calls in handlers and expressions

Tracking calls written in the template itself are read, not only event attributes:

<template>
  <button @click="() => { open(); trackEvent('plan:cogm_open', { id }) }">Open</button>
  <p>{{ trackEvent('panel:viewed') }}</p>
</template>

Read: @click / v-on handlers (one or several statements, multi-line arrow functions), {{ }} interpolations the source of v-for (item in items()), dynamic arguments (:[name()], @[event()]) and the expressions of v-bind, v-if, v-else-if, v-show, v-model, v-html and v-text. They are placed in the same module as the script, so a function imported or defined there resolves normally; an argument that comes from a v-for variable is reported as a dynamic occurrence. The loop variables of v-for and v-slot parameters are not code and are skipped; the value of a custom directive (v-focus="x") is left alone.

Nuxt

Nuxt pages, layouts, and components are ordinary .vue SFCs — parsed identically, no extra configuration needed.

Composables auto-imported without an explicit import statement (Nuxt's own composables, or your own from composables//utils/) are intended to resolve once the project has run nuxt prepare/nuxt dev (so .nuxt/ exists and is wired into tsconfig.json - the CLI analyzes the project's real tsconfig.json, so Nuxt's generated ambient types are visible to it like any other TypeScript project). The host CLI's resolver understands the declare const x: typeof import("./module")["name"] shape Nuxt/unplugin-auto-import generate for these bindings (covered by a unit test with a hand-written fixture of that ambient shape, and verified manually end to end on a real Nuxt 3 project: after nuxt prepare, calls to an auto-imported composable that wraps track() were detected with no explicit import; without .nuxt/ the same project yields no events). The end-to-end check is manual, not part of the automated test suite.


Configuration

No plugin-specific config — it activates purely by being listed in eventra.json's plugins array (see Installation).


Plugin contract

export interface CliPluginVue {
  readonly id: string;
  readonly version: string;
  readonly includeGlobs: readonly string[];
  readonly staticSinks?: readonly CliPluginStaticCalleeSink[];
  match(path: string): boolean;
  transform(input: { path: string; source: string }): Promise<{
    modules: Array<{ path: string; content: string }>;
  }>;
}

No dependency on @eventra_dev/eventra-cli - the CLI adapts this shape internally. .vue → one virtual .vue.ts module (compiled script content, then a function wrapping synthetic calls for every template event="..." binding and the handlers, interpolations and directive expressions copied from the template, under a // --- vue template (auto-generated) --- comment the CLI uses to tell generated code from your own lines). staticSinks describes those synthetic calls; the CLI builds its own sink detector from them. See @eventra_dev/eventra-cli's plugin docs for the full external-plugin contract.


Requirements

  • Node.js 18+
  • @eventra_dev/eventra-cli as the host CLI

Test Coverage

100% statement/branch/function/line coverage (v8 provider, pnpm test:coverage), enforced via a coverage.thresholds block in vitest.config.ts.

51 unit tests (vitest), covering:

Area Covers
SFC parsing Script + script-setup merge order, comment safety, src= external files
Template — literal Nested elements, v-if/v-for, slots, multiple template roots
Template — dynamic Raw expression passthrough, resolution through the merged script scope
Script-setup macros All five compiler macros (defineProps/defineEmits/defineExpose/defineOptions/withDefaults) pass through without special-casing
Virtual module output Single combined .vue.ts, export stub when script/template are empty
Plugin contract match(), includeGlobs, staticSinks, transform()

The Nuxt ambient auto-import shape (declare const x: typeof import(...)) is covered by a unit test in the host CLI that hand-writes that shape. A real nuxt prepare project was also checked end to end by hand, but that check is not part of the automated suite (see the Nuxt section above).

Run locally:

pnpm --filter @eventra_dev/cli-plugin-vue test
pnpm --filter @eventra_dev/cli-plugin-vue test:coverage

License

MIT, copyright (c) 2026 and-1991. See LICENSE.

Related Packages

@piwikpro/nuxt-piwik-pro

Dedicated Piwik PRO library that helps with implementing Piwik PRO Tag Manager a...

9,831
vue-plausible

Plausible analytics as Vue.js and NuxtJS plugins

73,595 118
@lg2/nuxt3-plugin-link-resolver

link resolver plugin fro nuxt3

3,544 0
@nuxtjs/plausible

Natively integrates Plausible Analytics into Nuxt

468,286 120
vue-hotjar

Vue Hotjar plugin

432,471 27

Version History

Version Released License
1.1.1 MIT
1.1.0 MIT
1.0.7 MIT
1.0.6 MIT
1.0.5 MIT