@eventra_dev/eventra-cli 1.0.5 → 2.0.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 CHANGED
@@ -206,11 +206,12 @@ pnpm add -D @eventra_dev/cli-plugin-vue
206
206
 
207
207
  The plugin:
208
208
 
209
- - splits each `.vue` file into virtual TypeScript modules (`App.vue.ts`, `App.vue.template.ts`)
210
- - extracts `track()` calls from `<script>` blocks
211
- - detects static `event="feature_name"` attributes in `<template>` (literals only)
209
+ - parses each `.vue` file with the real Vue compiler (`@vue/compiler-sfc`) into a single virtual TypeScript module combining `<script>`/`<script setup>`
210
+ - extracts `track()` calls from the script exactly like a regular `.ts` file (direct calls, wrappers, propagation)
211
+ - detects `event="feature_name"` attributes in `<template>` — both literal and dynamic (`:event="expr"`); dynamic bindings resolve through the same module scope as the script, or surface as a dynamic occurrence if unresolvable
212
+ - Nuxt `.vue` files (pages/layouts/components) are handled the same way; auto-imported composables resolve once the project has run `nuxt prepare`/`nuxt dev`
212
213
 
213
- `sync.include` does **not** need `**/*.vue` manually — the plugin registers `**/*.vue` via `includeGlobs`.
214
+ `sync.include` does **not** need `**/*.vue` manually — the plugin registers `**/*.vue` via `includeGlobs`. See [`@eventra_dev/cli-plugin-vue`](https://www.npmjs.com/package/@eventra_dev/cli-plugin-vue) for details.
214
215
 
215
216
  ### Before publishing / local development
216
217
 
@@ -15,6 +15,42 @@ function isConcreteDeclaration(node) {
15
15
  || typescript_1.default.isMethodDeclaration(node)
16
16
  || typescript_1.default.isPropertyDeclaration(node));
17
17
  }
18
+ /**
19
+ * `declare const x: typeof import("./module")["name"]` (or the dotted form,
20
+ * `typeof import("./module").name`) is how Nuxt/unplugin-auto-import
21
+ * generate global ambient bindings for auto-imported exports. A `const`
22
+ * with no initializer is only legal in an ambient context, so this shape is
23
+ * unambiguous. Without unwrapping it, this ambient variable is its own
24
+ * distinct symbol — the wrapper/export registered under the *real*
25
+ * declaration's symbol would never match a call site that resolves to the
26
+ * ambient one instead.
27
+ */
28
+ function resolveAmbientAutoImportSymbol(symbol, checker) {
29
+ const declarations = symbol.getDeclarations();
30
+ if (!declarations || declarations.length !== 1) {
31
+ return null;
32
+ }
33
+ const [declaration] = declarations;
34
+ if (!typescript_1.default.isVariableDeclaration(declaration) || declaration.initializer || !declaration.type) {
35
+ return null;
36
+ }
37
+ let nameNode;
38
+ const type = declaration.type;
39
+ if (typescript_1.default.isImportTypeNode(type) && type.qualifier && typescript_1.default.isIdentifier(type.qualifier)) {
40
+ nameNode = type.qualifier;
41
+ }
42
+ else if (typescript_1.default.isIndexedAccessTypeNode(type)
43
+ && typescript_1.default.isImportTypeNode(type.objectType)
44
+ && typescript_1.default.isLiteralTypeNode(type.indexType)
45
+ && typescript_1.default.isStringLiteral(type.indexType.literal)) {
46
+ nameNode = type.indexType.literal;
47
+ }
48
+ if (!nameNode) {
49
+ return null;
50
+ }
51
+ const resolved = checker.getSymbolAtLocation(nameNode);
52
+ return resolved && resolved !== symbol ? resolved : null;
53
+ }
18
54
  function resolveExportedSymbol(symbol, checker, cache, visited) {
19
55
  if (typeof symbol.getDeclarations !== "function") {
20
56
  return null;
@@ -44,6 +80,13 @@ function resolveExportedSymbol(symbol, checker, cache, visited) {
44
80
  cache.set(symbol, resolved);
45
81
  return resolved;
46
82
  }
83
+ // ambient auto-import unwrap (Nuxt/unplugin-auto-import style globals)
84
+ const ambientTarget = resolveAmbientAutoImportSymbol(symbol, checker);
85
+ if (ambientTarget) {
86
+ const resolved = resolveExportedSymbol(ambientTarget, checker, cache, seen);
87
+ cache.set(symbol, resolved);
88
+ return resolved;
89
+ }
47
90
  // declaration existence
48
91
  const declarations = symbol.getDeclarations() ?? [];
49
92
  for (const declaration of declarations) {
@@ -18,20 +18,6 @@ function isTrackCallee(expr) {
18
18
  }
19
19
  return false;
20
20
  }
21
- function fileImportsEventraSdk(sourceFile) {
22
- for (const stmt of sourceFile.statements) {
23
- if (!typescript_1.default.isImportDeclaration(stmt) || !stmt.moduleSpecifier) {
24
- continue;
25
- }
26
- if (!typescript_1.default.isStringLiteral(stmt.moduleSpecifier)) {
27
- continue;
28
- }
29
- if ((0, eventraSdk_1.isEventraSdkModuleSpecifier)(stmt.moduleSpecifier.text)) {
30
- return true;
31
- }
32
- }
33
- return false;
34
- }
35
21
  function getImportDeclarationForSpecifier(decl) {
36
22
  let current = decl;
37
23
  while (current) {
@@ -155,10 +141,6 @@ function isEventraSdkTrackCall(call, checker) {
155
141
  if (!isTrackCallee(expr)) {
156
142
  return false;
157
143
  }
158
- const sourceFile = call.getSourceFile();
159
- if (!fileImportsEventraSdk(sourceFile)) {
160
- return false;
161
- }
162
144
  const receiver = typescript_1.default.isPropertyAccessExpression(expr) ||
163
145
  typescript_1.default.isPropertyAccessChain(expr) ||
164
146
  typescript_1.default.isElementAccessExpression(expr)
package/dist/cli/send.js CHANGED
@@ -62,6 +62,7 @@ async function send(opts = {}) {
62
62
  const config = await (0, config_1.loadConfig)();
63
63
  if (!config) {
64
64
  console.log(chalk_1.default.red("eventra.json not found. Run 'eventra init'"));
65
+ process.exitCode = 1;
65
66
  return;
66
67
  }
67
68
  const endpoint = config.endpoint || process.env.EVENTRA_ENDPOINT || undefined;
@@ -79,6 +80,7 @@ async function send(opts = {}) {
79
80
  console.log(chalk_1.default.red(`Endpoint "${endpoint}" from eventra.json is not locally approved.`));
80
81
  console.log(chalk_1.default.gray("If this is expected (e.g. a self-hosted ingest URL), approve it once with:"));
81
82
  console.log(chalk_1.default.gray(" eventra send --trust-endpoint"));
83
+ process.exitCode = 1;
82
84
  return;
83
85
  }
84
86
  await (0, config_1.trustEndpoint)(endpoint);
@@ -90,6 +92,7 @@ async function send(opts = {}) {
90
92
  if (!apiKey) {
91
93
  if (!process.stdin.isTTY) {
92
94
  console.log(chalk_1.default.red("API key required. Set the EVENTRA_API_KEY environment variable (recommended for CI) or run 'eventra init'."));
95
+ process.exitCode = 1;
93
96
  return;
94
97
  }
95
98
  const answers = await inquirer_1.default.prompt([
@@ -110,14 +113,17 @@ async function send(opts = {}) {
110
113
  // no events
111
114
  if (!config.events?.length) {
112
115
  console.log(chalk_1.default.yellow("No events found. Run 'eventra sync'"));
116
+ process.exitCode = 1;
113
117
  return;
114
118
  }
115
119
  if (!apiKey) {
116
120
  console.log(chalk_1.default.red("API key required"));
121
+ process.exitCode = 1;
117
122
  return;
118
123
  }
119
124
  if (!endpoint) {
120
125
  console.log(chalk_1.default.red("Endpoint not configured"));
126
+ process.exitCode = 1;
121
127
  return;
122
128
  }
123
129
  console.log("");
@@ -145,6 +151,7 @@ async function send(opts = {}) {
145
151
  console.log(chalk_1.default.red(`Request failed (${outcome.status})`));
146
152
  if (outcome.body)
147
153
  console.log(chalk_1.default.gray(outcome.body));
154
+ process.exitCode = 1;
148
155
  return;
149
156
  }
150
157
  lastFailure = { status: outcome.status, body: outcome.body };
@@ -173,6 +180,7 @@ async function send(opts = {}) {
173
180
  else if (lastFailure?.error instanceof Error) {
174
181
  console.log(chalk_1.default.gray(lastFailure.error.message));
175
182
  }
183
+ process.exitCode = 1;
176
184
  return;
177
185
  }
178
186
  console.log(chalk_1.default.green("Events registered successfully"));
package/dist/cli/watch.js CHANGED
@@ -35,6 +35,14 @@ async function watch() {
35
35
  const normalize = (file) => {
36
36
  return path_1.default.resolve(file).replace(/\\/g, "/");
37
37
  };
38
+ // chokidar's `ignored` receives absolute, normalized paths — reject any path
39
+ // with an excluded directory name as one of its segments (matches how
40
+ // `fast-glob`'s own `ignore` option is applied during the initial scan).
41
+ const excludedDirs = new Set(config.sync.exclude);
42
+ const isExcludedPath = (file) => {
43
+ const segments = normalize(file).split("/");
44
+ return segments.some((segment) => excludedDirs.has(segment));
45
+ };
38
46
  const files = await (0, fast_glob_1.default)(include, {
39
47
  ignore: config.sync.exclude,
40
48
  absolute: true,
@@ -68,7 +76,15 @@ async function watch() {
68
76
  const initialEvents = engine.getAllEvents();
69
77
  const initialWrappers = engine.getAllFunctionWrappers();
70
78
  console.log(chalk_1.default.gray(`Initial: ${initialEvents.length} events, ${initialWrappers.length} wrappers\n`));
71
- const watcher = chokidar_1.default.watch([...sourceFiles, ...watchedDeps], { ignoreInitial: true });
79
+ // Watch the project root (not just the files found by the initial scan) so
80
+ // that brand-new files matching `include` are noticed as they're created —
81
+ // a fixed file list only ever detects edits to files chokidar already knows
82
+ // about. Dependency files outside the root (added below via `watcher.add`)
83
+ // are unaffected by this and keep working the same way.
84
+ const watcher = chokidar_1.default.watch(process.cwd(), {
85
+ ignoreInitial: true,
86
+ ignored: isExcludedPath,
87
+ });
72
88
  const queue = new Map();
73
89
  let timer = null;
74
90
  const run = async () => {
@@ -48,10 +48,6 @@ function createStaticCalleeSinkDetector(sink) {
48
48
  if (call.arguments.length <= argIndex) {
49
49
  return null;
50
50
  }
51
- const arg = call.arguments[argIndex];
52
- if (!typescript_1.default.isStringLiteral(arg)) {
53
- return null;
54
- }
55
51
  return {
56
52
  call,
57
53
  trackedArguments: [{ index: argIndex, propertyPath: [] }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "1.0.5",
3
+ "version": "2.0.0",
4
4
  "description": "Static analytics event extraction CLI powered by the TypeScript compiler API",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",