@eventra_dev/eventra-cli 0.0.19 → 0.1.2

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.
Files changed (41) hide show
  1. package/dist/commands/check.js +17 -117
  2. package/dist/commands/sync.js +20 -115
  3. package/dist/commands/watch.js +107 -69
  4. package/dist/engine/callResolver.js +34 -0
  5. package/dist/engine/engine.js +47 -0
  6. package/dist/engine/graph.js +25 -0
  7. package/dist/engine/hash.js +10 -0
  8. package/dist/engine/languageService.js +67 -0
  9. package/dist/engine/resolver.js +69 -0
  10. package/dist/engine/scanner.js +50 -0
  11. package/dist/utils/parsers/vue.js +16 -12
  12. package/dist/utils/processFile.js +20 -0
  13. package/package.json +1 -1
  14. package/src/commands/check.ts +19 -151
  15. package/src/commands/sync.ts +23 -194
  16. package/src/commands/watch.ts +119 -66
  17. package/src/engine/callResolver.ts +43 -0
  18. package/src/engine/engine.ts +60 -0
  19. package/src/engine/graph.ts +23 -0
  20. package/src/engine/hash.ts +5 -0
  21. package/src/engine/languageService.ts +73 -0
  22. package/src/engine/resolver.ts +86 -0
  23. package/src/engine/scanner.ts +61 -0
  24. package/src/types.ts +5 -6
  25. package/src/utils/parsers/vue.ts +19 -20
  26. package/src/utils/processFile.ts +18 -0
  27. package/tests/fixtures/backend/express/app.ts +167 -12
  28. package/tests/fixtures/wrappers/component/test.tsx +1 -1
  29. package/tests/run.ts +4 -17
  30. package/dist/utils/detect-aliases.js +0 -39
  31. package/dist/utils/detect-wrappers.js +0 -84
  32. package/dist/utils/extract.js +0 -56
  33. package/dist/utils/scanners/component-wrappers.js +0 -73
  34. package/dist/utils/scanners/function-wrappers.js +0 -83
  35. package/dist/utils/scanners/track.js +0 -64
  36. package/src/utils/detect-aliases.ts +0 -46
  37. package/src/utils/detect-wrappers.ts +0 -119
  38. package/src/utils/extract.ts +0 -72
  39. package/src/utils/scanners/component-wrappers.ts +0 -109
  40. package/src/utils/scanners/function-wrappers.ts +0 -137
  41. package/src/utils/scanners/track.ts +0 -99
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TSService = void 0;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ class TSService {
10
+ root;
11
+ files = new Map();
12
+ service;
13
+ constructor(root) {
14
+ this.root = root;
15
+ const host = {
16
+ getScriptFileNames: () => [...this.files.keys()],
17
+ getScriptVersion: (file) => this.files.get(file)?.version.toString() ?? "0",
18
+ getScriptSnapshot: (file) => {
19
+ const f = this.files.get(file);
20
+ if (f) {
21
+ return typescript_1.default.ScriptSnapshot.fromString(f.content);
22
+ }
23
+ if (fs_1.default.existsSync(file)) {
24
+ return typescript_1.default.ScriptSnapshot.fromString(fs_1.default.readFileSync(file, "utf-8"));
25
+ }
26
+ return undefined;
27
+ },
28
+ getCurrentDirectory: () => this.root,
29
+ getCompilationSettings: () => ({
30
+ allowJs: true,
31
+ jsx: typescript_1.default.JsxEmit.Preserve,
32
+ moduleResolution: typescript_1.default.ModuleResolutionKind.Node16,
33
+ target: typescript_1.default.ScriptTarget.ESNext,
34
+ esModuleInterop: true,
35
+ allowSyntheticDefaultImports: true,
36
+ }),
37
+ getDefaultLibFileName: (opts) => typescript_1.default.getDefaultLibFilePath(opts),
38
+ readFile: (fileName, encoding) => {
39
+ try {
40
+ return fs_1.default.readFileSync(fileName, encoding);
41
+ }
42
+ catch (err) {
43
+ return undefined;
44
+ }
45
+ },
46
+ fileExists: fs_1.default.existsSync,
47
+ };
48
+ this.service = typescript_1.default.createLanguageService(host);
49
+ }
50
+ updateFile(file, content) {
51
+ const prev = this.files.get(file);
52
+ this.files.set(file, {
53
+ content,
54
+ version: prev ? prev.version + 1 : 1,
55
+ });
56
+ }
57
+ getProgram() {
58
+ return this.service.getProgram();
59
+ }
60
+ getChecker() {
61
+ return this.getProgram()?.getTypeChecker();
62
+ }
63
+ getSourceFile(file) {
64
+ return this.getProgram()?.getSourceFile(file);
65
+ }
66
+ }
67
+ exports.TSService = TSService;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveNodeValue = resolveNodeValue;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ function resolveNodeValue(node, checker) {
9
+ if (typescript_1.default.isStringLiteral(node)) {
10
+ return { values: [node.text], dynamic: false };
11
+ }
12
+ if (typescript_1.default.isTemplateExpression(node)) {
13
+ let result = node.head.text;
14
+ for (const span of node.templateSpans) {
15
+ result += "*";
16
+ result += span.literal.text;
17
+ }
18
+ return { values: [result], dynamic: true };
19
+ }
20
+ if (typescript_1.default.isConditionalExpression(node)) {
21
+ const a = resolveNodeValue(node.whenTrue, checker);
22
+ const b = resolveNodeValue(node.whenFalse, checker);
23
+ return {
24
+ values: [...new Set([...(a?.values ?? []), ...(b?.values ?? [])])],
25
+ dynamic: true,
26
+ };
27
+ }
28
+ if (typescript_1.default.isIdentifier(node)) {
29
+ let symbol = checker.getSymbolAtLocation(node);
30
+ if (!symbol)
31
+ return null;
32
+ if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
33
+ symbol = checker.getAliasedSymbol(symbol);
34
+ }
35
+ for (const decl of symbol.getDeclarations() ?? []) {
36
+ if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
37
+ return resolveNodeValue(decl.initializer, checker);
38
+ }
39
+ if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
40
+ if (typescript_1.default.isStringLiteral(decl.initializer)) {
41
+ return {
42
+ values: [decl.initializer.text],
43
+ dynamic: false,
44
+ };
45
+ }
46
+ }
47
+ }
48
+ }
49
+ if (typescript_1.default.isPropertyAccessExpression(node)) {
50
+ const symbol = checker.getSymbolAtLocation(node);
51
+ if (symbol) {
52
+ let s = symbol;
53
+ if (s.flags & typescript_1.default.SymbolFlags.Alias) {
54
+ s = checker.getAliasedSymbol(s);
55
+ }
56
+ for (const decl of s.getDeclarations() ?? []) {
57
+ if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
58
+ if (typescript_1.default.isStringLiteral(decl.initializer)) {
59
+ return {
60
+ values: [decl.initializer.text],
61
+ dynamic: false,
62
+ };
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ return null;
69
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scanSource = scanSource;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const resolver_1 = require("./resolver");
9
+ const callResolver_1 = require("./callResolver");
10
+ function scanSource(source, checker, visited = new Set()) {
11
+ const events = new Set();
12
+ if (visited.has(source.fileName))
13
+ return events;
14
+ visited.add(source.fileName);
15
+ function visit(node) {
16
+ if (typescript_1.default.isCallExpression(node)) {
17
+ const name = getCallName(node.expression);
18
+ if (/(track|event|capture|send)/i.test(name)) {
19
+ let target = node.arguments[0];
20
+ const fn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
21
+ // 🔥 cross-file
22
+ if (fn) {
23
+ const sf = fn.getSourceFile();
24
+ if (!visited.has(sf.fileName)) {
25
+ scanSource(sf, checker, visited).forEach(e => events.add(e));
26
+ }
27
+ }
28
+ if (target) {
29
+ const res = (0, resolver_1.resolveNodeValue)(target, checker);
30
+ res?.values.forEach(v => {
31
+ if (v && v.length > 1 && !v.includes(" ")) {
32
+ events.add(v);
33
+ }
34
+ });
35
+ }
36
+ }
37
+ }
38
+ typescript_1.default.forEachChild(node, visit);
39
+ }
40
+ visit(source);
41
+ return events;
42
+ }
43
+ function getCallName(expr) {
44
+ if (typescript_1.default.isIdentifier(expr))
45
+ return expr.text;
46
+ if (typescript_1.default.isPropertyAccessExpression(expr)) {
47
+ return expr.name.text;
48
+ }
49
+ return "";
50
+ }
@@ -2,17 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseVue = parseVue;
4
4
  function parseVue(content) {
5
- const template = content.match(/<template[\s\S]*?>([\s\S]*?)<\/template>/);
6
- const script = content.match(/<script[\s\S]*?>([\s\S]*?)<\/script>/);
7
- return `
8
- ${script?.[1] ?? ""}
9
-
10
- function __vue_template__() {
11
- return (
12
- <>
13
- ${template?.[1] ?? ""}
14
- </>
15
- );
16
- }
5
+ const scriptMatch = content.match(/<script[\s\S]*?>([\s\S]*?)<\/script>/);
6
+ const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
7
+ const script = scriptMatch?.[1] ?? "";
8
+ let template = templateMatch?.[1] ?? "";
9
+ template = template.replace(/<!--[\s\S]*?-->/g, "");
10
+ template = template
11
+ .replace(/v-if="[^"]+"/g, "")
12
+ .replace(/v-for="[^"]+"/g, "")
13
+ .replace(/@click=/g, "onClick=");
14
+ const jsx = `
15
+ const __VUE_JSX__ = (
16
+ <>
17
+ ${template}
18
+ </>
19
+ );
17
20
  `;
21
+ return script + "\n" + jsx;
18
22
  }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processFile = processFile;
4
+ const router_1 = require("./parsers/router");
5
+ const vue_1 = require("./parsers/vue");
6
+ const svelte_1 = require("./parsers/svelte");
7
+ const astro_1 = require("./parsers/astro");
8
+ function processFile(file, content) {
9
+ const parser = (0, router_1.detectParser)(file);
10
+ if (parser === "vue")
11
+ content = (0, vue_1.parseVue)(content);
12
+ if (parser === "svelte")
13
+ content = (0, svelte_1.parseSvelte)(content);
14
+ if (parser === "astro")
15
+ content = (0, astro_1.parseAstro)(content);
16
+ const virtualFile = file.endsWith(".vue")
17
+ ? file + ".tsx"
18
+ : file;
19
+ return { content, virtualFile };
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.0.19",
3
+ "version": "0.1.2",
4
4
  "description": "Eventra CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,21 +1,10 @@
1
1
  import chalk from "chalk";
2
2
  import fg from "fast-glob";
3
- import { Project } from "ts-morph";
4
3
  import fs from "fs/promises";
5
- import inquirer from "inquirer";
6
4
 
7
5
  import { loadConfig, saveConfig } from "../utils/config";
8
-
9
- import { detectParser } from "../utils/parsers/router";
10
- import { parseVue } from "../utils/parsers/vue";
11
- import { parseSvelte } from "../utils/parsers/svelte";
12
- import { parseAstro } from "../utils/parsers/astro";
13
-
14
- import { scanTrack } from "../utils/scanners/track";
15
- import { scanFunctionWrappers } from "../utils/scanners/function-wrappers";
16
- import { scanComponentWrappers } from "../utils/scanners/component-wrappers";
17
-
18
- import { ExtractedEvent } from "../types";
6
+ import { EventraEngine } from "../engine/engine";
7
+ import { processFile } from "../utils/processFile";
19
8
 
20
9
  export async function check({ fix = false }: { fix?: boolean }) {
21
10
  const config = await loadConfig();
@@ -27,169 +16,48 @@ export async function check({ fix = false }: { fix?: boolean }) {
27
16
 
28
17
  console.log(chalk.blue("Checking events..."));
29
18
 
30
- const project = new Project();
31
-
32
- const knownEvents = new Set(config.events ?? []);
33
- const aliases = { ...(config.aliases ?? {}) };
34
-
35
- const foundEvents = new Map<string, ExtractedEvent>();
36
- const newEvents = new Map<string, ExtractedEvent>();
37
- const unresolvedDynamic = new Map<string, ExtractedEvent>();
38
-
39
19
  const files = await fg(config.sync.include, {
40
- ignore: config.sync.exclude
20
+ ignore: config.sync.exclude,
41
21
  });
42
22
 
23
+ const engine = new EventraEngine(process.cwd());
24
+
43
25
  for (const file of files) {
44
26
  try {
45
- const stat = await fs.stat(file);
46
-
47
- if (stat.size > 2 * 1024 * 1024) {
48
- continue;
49
- }
50
-
51
- const parser = detectParser(file);
52
- let content = await fs.readFile(file, "utf-8");
53
-
54
- if (parser === "vue") content = parseVue(content);
55
- if (parser === "svelte") content = parseSvelte(content);
56
- if (parser === "astro") content = parseAstro(content);
57
-
58
- const source = project.createSourceFile(file, content, {
59
- overwrite: true
60
- });
61
-
62
- const found: ExtractedEvent[] = [
63
- ...scanTrack(source, config, aliases),
64
- ...scanFunctionWrappers(source, config.functionWrappers ?? [], aliases),
65
- ...scanComponentWrappers(source, config.wrappers ?? [], aliases)
66
- ];
67
-
68
- for (const event of found) {
69
- const { value, dynamic } = event;
70
- if (!value) continue;
71
-
72
- // skip
73
- if (aliases[value] === "__skip__") continue;
74
-
75
- // alias resolve
76
- if (value in aliases) {
77
- foundEvents.set(aliases[value], {
78
- ...event,
79
- value: aliases[value]
80
- });
81
- continue;
82
- }
83
-
84
- if (dynamic) {
85
- unresolvedDynamic.set(value, event);
86
- continue;
87
- }
27
+ const raw = await fs.readFile(file, "utf-8");
88
28
 
89
- foundEvents.set(value, event);
90
- }
29
+ const { content, virtualFile } = processFile(file, raw);
91
30
 
92
- project.removeSourceFile(source);
31
+ engine.scanFile(virtualFile, content);
93
32
 
94
33
  } catch {
95
34
  console.log(chalk.gray(`Skipped: ${file}`));
96
35
  }
97
36
  }
98
37
 
99
- // find new
100
- for (const [key, event] of foundEvents) {
101
- if (!knownEvents.has(key)) {
102
- newEvents.set(key, event);
103
- }
104
- }
105
-
106
- // FIX MODE
107
- if (fix) {
108
- console.log(chalk.yellow("\nFix mode\n"));
38
+ const found = new Set(engine.getAllEvents());
39
+ const known = new Set(config.events ?? []);
109
40
 
110
- // add new events
111
- for (const [name] of newEvents) {
112
- config.events.push(name);
113
- console.log(chalk.green(`+ ${name}`));
114
- }
115
-
116
- // resolve dynamic
117
- for (const [value] of unresolvedDynamic) {
118
- const { action } = await inquirer.prompt([
119
- {
120
- type: "list",
121
- name: "action",
122
- message: `Dynamic: ${value}`,
123
- choices: [
124
- { name: "Alias", value: "alias" },
125
- { name: "Skip", value: "skip" }
126
- ]
127
- }
128
- ]);
129
-
130
- if (action === "skip") {
131
- aliases[value] = "__skip__";
132
- continue;
133
- }
134
-
135
- const { name } = await inquirer.prompt([
136
- {
137
- type: "input",
138
- name: "name",
139
- message: "Event name:"
140
- }
141
- ]);
142
-
143
- const normalized = name.trim().toLowerCase();
144
- if (!normalized) continue;
145
-
146
- aliases[value] = normalized;
147
- config.events.push(normalized);
148
-
149
- console.log(chalk.green(`${value} → ${normalized}`));
150
- }
41
+ const newEvents = [...found].filter(e => !known.has(e));
151
42
 
152
- config.aliases = Object.fromEntries(
153
- Object.entries(aliases).sort()
154
- );
43
+ if (fix) {
44
+ newEvents.forEach(e => {
45
+ config.events.push(e);
46
+ console.log(chalk.green(`+ ${e}`));
47
+ });
155
48
 
156
49
  config.events = [...new Set(config.events)].sort();
157
50
 
158
51
  await saveConfig(config);
159
52
 
160
- console.log(chalk.green("\n✔ Fixed"));
53
+ console.log(chalk.green("\nFixed"));
161
54
  return;
162
55
  }
163
56
 
164
- // NORMAL MODE
165
- let hasError = false;
166
-
167
- if (unresolvedDynamic.size) {
168
- hasError = true;
169
-
170
- console.log(chalk.red("\nDynamic events:"));
171
-
172
- unresolvedDynamic.forEach((e) =>
173
- console.log(
174
- chalk.red(`- ${e.value} (${e.file}:${e.line})`)
175
- )
176
- );
177
- }
178
-
179
- if (newEvents.size) {
180
- hasError = true;
181
-
57
+ if (newEvents.length) {
182
58
  console.log(chalk.red("\nNew events:"));
59
+ newEvents.forEach(e => console.log(chalk.red(`+ ${e}`)));
183
60
 
184
- newEvents.forEach((e) =>
185
- console.log(
186
- chalk.red(`+ ${e.value} (${e.file}:${e.line})`)
187
- )
188
- );
189
- }
190
-
191
- if (hasError) {
192
- console.log(chalk.red("\nEvent check failed"));
193
61
  process.exit(1);
194
62
  }
195
63