@eventra_dev/eventra-cli 0.1.31 → 0.1.33

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.
@@ -5,23 +5,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.parseUniversal = parseUniversal;
7
7
  const path_1 = __importDefault(require("path"));
8
- // SAFE COMMENT STRIP
8
+ /* ---------------------------------- */
9
+ /* COMMENT STRIP (SAFE) */
10
+ /* ---------------------------------- */
9
11
  function stripComments(input) {
10
12
  return input
11
13
  .replace(/<!--[\s\S]*?-->/g, "")
12
14
  .replace(/\/\*[\s\S]*?\*\//g, "");
13
15
  }
14
- // TRACK FILTER
16
+ /* ---------------------------------- */
17
+ /* TRACK DETECTOR */
18
+ /* ---------------------------------- */
15
19
  function looksLikeTracking(code) {
16
- return /\btrack\w*\s*\(/.test(code);
20
+ return /\btrack\s*\(|\.track\s*\(/.test(code);
17
21
  }
18
- // SAFE {} PARSER
22
+ /* ---------------------------------- */
23
+ /* SAFE {} PARSER (JSX / TEMPLATES) */
24
+ /* ---------------------------------- */
19
25
  function extractBracedExpressions(input) {
20
26
  const result = [];
21
27
  let depth = 0;
22
28
  let start = -1;
29
+ let inString = null;
23
30
  for (let i = 0; i < input.length; i++) {
24
31
  const char = input[i];
32
+ // handle strings
33
+ if (inString) {
34
+ if (char === inString && input[i - 1] !== "\\") {
35
+ inString = null;
36
+ }
37
+ continue;
38
+ }
39
+ if (char === '"' || char === "'" || char === "`") {
40
+ inString = char;
41
+ continue;
42
+ }
25
43
  if (char === "{") {
26
44
  if (depth === 0)
27
45
  start = i + 1;
@@ -40,7 +58,9 @@ function extractBracedExpressions(input) {
40
58
  }
41
59
  return result;
42
60
  }
43
- // HTML handlers
61
+ /* ---------------------------------- */
62
+ /* HTML HANDLERS */
63
+ /* ---------------------------------- */
44
64
  function extractHTMLHandlers(input) {
45
65
  const result = [];
46
66
  const regex = /\bon\w+\s*=\s*["']([^"']+)["']/g;
@@ -51,7 +71,9 @@ function extractHTMLHandlers(input) {
51
71
  }
52
72
  return result;
53
73
  }
54
- // VUE handlers
74
+ /* ---------------------------------- */
75
+ /* VUE HANDLERS */
76
+ /* ---------------------------------- */
55
77
  function extractVueHandlers(input) {
56
78
  const result = [];
57
79
  const regex = /(?:@|v-on:)[\w:-]+\s*=\s*["']([^"']+)["']/g;
@@ -62,23 +84,27 @@ function extractVueHandlers(input) {
62
84
  }
63
85
  return result;
64
86
  }
65
- // SVELTE handlers
87
+ /* ---------------------------------- */
88
+ /* SVELTE HANDLERS */
89
+ /* ---------------------------------- */
66
90
  function extractSvelteHandlers(input) {
67
91
  const result = [];
68
- const regex = /\bon:[\w]+\s*=\s*{([^}]+)}/g;
92
+ const regex = /\bon:[\w]+\s*=\s*{([\s\S]*?)}/g;
69
93
  for (const m of input.matchAll(regex)) {
70
- if (looksLikeTracking(m[1])) {
71
- result.push(m[1]);
72
- }
94
+ const expr = m[1];
95
+ const nested = extractBracedExpressions(`{${expr}}`);
96
+ result.push(...nested);
73
97
  }
74
98
  return result;
75
99
  }
76
- // SCRIPT EXTRACTION
100
+ /* ---------------------------------- */
101
+ /* SCRIPT EXTRACTION */
102
+ /* ---------------------------------- */
77
103
  function extractScripts(input, file) {
78
104
  const scripts = [];
79
105
  const deps = [];
80
- // src scripts
81
- const srcRegex = /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/g;
106
+ // <script src="">
107
+ const srcRegex = /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/gi;
82
108
  for (const m of input.matchAll(srcRegex)) {
83
109
  const src = m[1];
84
110
  const resolved = src.startsWith("/")
@@ -86,8 +112,8 @@ function extractScripts(input, file) {
86
112
  : path_1.default.resolve(path_1.default.dirname(file), src);
87
113
  deps.push(resolved);
88
114
  }
89
- // inline scripts
90
- const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/g;
115
+ // inline <script>
116
+ const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
91
117
  for (const m of input.matchAll(scriptRegex)) {
92
118
  if (m[1]?.trim()) {
93
119
  scripts.push(m[1]);
@@ -96,9 +122,27 @@ function extractScripts(input, file) {
96
122
  const noScripts = input.replace(scriptRegex, "");
97
123
  return { scripts, deps, noScripts };
98
124
  }
99
- // DYNAMIC IMPORTS
125
+ /* ---------------------------------- */
126
+ /* STATIC IMPORTS */
127
+ /* ---------------------------------- */
128
+ function extractStaticImports(input, file) {
129
+ const deps = [];
130
+ const regex = /import\s+(?:.+?\s+from\s+)?["'](.+?)["']/g;
131
+ for (const m of input.matchAll(regex)) {
132
+ const mod = m[1];
133
+ if (!mod.startsWith(".") && !mod.startsWith("/"))
134
+ continue;
135
+ const resolved = path_1.default.resolve(path_1.default.dirname(file), mod);
136
+ deps.push(resolved);
137
+ }
138
+ return deps;
139
+ }
140
+ /* ---------------------------------- */
141
+ /* DYNAMIC IMPORTS */
142
+ /* ---------------------------------- */
100
143
  function extractDynamicImports(input, file) {
101
144
  const deps = [];
145
+ const calls = [];
102
146
  const regex = /import\(\s*["'](.+?)["']\s*\)/g;
103
147
  for (const m of input.matchAll(regex)) {
104
148
  const mod = m[1];
@@ -106,16 +150,21 @@ function extractDynamicImports(input, file) {
106
150
  ? path_1.default.resolve(process.cwd(), "." + mod)
107
151
  : path_1.default.resolve(path_1.default.dirname(file), mod);
108
152
  deps.push(resolved);
153
+ if (looksLikeTracking(mod)) {
154
+ calls.push(`import("${mod}")`);
155
+ }
109
156
  }
110
- return deps;
157
+ return { deps, calls };
111
158
  }
112
- // MAIN
159
+ /* ---------------------------------- */
160
+ /* MAIN */
161
+ /* ---------------------------------- */
113
162
  function parseUniversal(content, file) {
114
163
  const parts = [];
115
164
  const deps = [];
116
165
  const clean = stripComments(content);
117
166
  /* ---------- ASTRO FRONTMATTER ---------- */
118
- const fm = clean.match(/^---([\s\S]*?)---/);
167
+ const fm = clean.match(/^\s*---([\s\S]*?)---/);
119
168
  if (fm?.[1]) {
120
169
  parts.push(fm[1]);
121
170
  }
@@ -130,7 +179,10 @@ function parseUniversal(content, file) {
130
179
  /* ---------- JSX / TEMPLATE ---------- */
131
180
  parts.push(...extractBracedExpressions(clean));
132
181
  /* ---------- IMPORTS ---------- */
133
- deps.push(...extractDynamicImports(clean, file));
182
+ deps.push(...extractStaticImports(clean, file));
183
+ const dyn = extractDynamicImports(clean, file);
184
+ deps.push(...dyn.deps);
185
+ parts.push(...dyn.calls);
134
186
  return {
135
187
  code: [...new Set(parts)].join("\n"),
136
188
  deps: [...new Set(deps.filter(Boolean))],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Eventra CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
2
 
3
+ // HELPERS
3
4
  function isFunctionLike(
4
5
  node: ts.Node
5
6
  ): node is ts.FunctionLikeDeclaration {
@@ -12,40 +13,39 @@ function isFunctionLike(
12
13
  );
13
14
  }
14
15
 
16
+ // symbol → function
15
17
  function resolveFromSymbol(
16
18
  symbol: ts.Symbol,
17
19
  checker: ts.TypeChecker,
18
20
  visited = new Set<ts.Symbol>(),
19
21
  depth = 0
20
22
  ): ts.FunctionLikeDeclaration | null {
23
+ if (!symbol) return null;
21
24
  if (visited.has(symbol)) return null;
22
- if (depth > 5) return null;
25
+ if (depth > 10) return null;
23
26
 
24
27
  visited.add(symbol);
25
28
 
26
- // unwrap alias
29
+ // Alias unwrap (import/export)
27
30
  if (symbol.flags & ts.SymbolFlags.Alias) {
28
- const aliased = checker.getAliasedSymbol(symbol);
29
- return resolveFromSymbol(aliased, checker, visited, depth + 1);
30
- }
31
-
32
- // re-export support
33
- if ((symbol as any).exports) {
34
- for (const exp of (symbol as any).exports.values()) {
35
- const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
36
- if (resolved) return resolved;
37
- }
31
+ try {
32
+ const aliased = checker.getAliasedSymbol(symbol);
33
+ return resolveFromSymbol(aliased, checker, visited, depth + 1);
34
+ } catch {}
38
35
  }
39
36
 
37
+ // Declarations
40
38
  const decls = symbol.getDeclarations() ?? [];
41
39
 
42
40
  for (const decl of decls) {
43
- // direct function
41
+ /**
42
+ * direct function
43
+ */
44
44
  if (isFunctionLike(decl)) {
45
45
  return decl;
46
46
  }
47
47
 
48
- // VARIABLE → FUNCTION
48
+ // VARIABLE → FUNCTION const fn = () => {}
49
49
  if (ts.isVariableDeclaration(decl) && decl.initializer) {
50
50
  if (isFunctionLike(decl.initializer)) {
51
51
  return decl.initializer;
@@ -63,7 +63,12 @@ function resolveFromSymbol(
63
63
  }
64
64
  }
65
65
 
66
- // MethodSignature (interface / type)
66
+ // CLASS METHOD
67
+ if (ts.isMethodDeclaration(decl)) {
68
+ return decl;
69
+ }
70
+
71
+ // interface / type method
67
72
  if (ts.isMethodSignature(decl)) {
68
73
  const parent = decl.parent;
69
74
 
@@ -71,14 +76,10 @@ function resolveFromSymbol(
71
76
  ts.isInterfaceDeclaration(parent) ||
72
77
  ts.isTypeLiteralNode(parent)
73
78
  ) {
74
- const name = decl.name.getText();
75
-
76
79
  const type = checker.getTypeAtLocation(parent);
77
80
  const props = type.getProperties();
78
81
 
79
82
  for (const prop of props) {
80
- if (prop.name !== name) continue;
81
-
82
83
  const resolved = resolveFromSymbol(
83
84
  prop,
84
85
  checker,
@@ -91,10 +92,24 @@ function resolveFromSymbol(
91
92
  }
92
93
 
93
94
  // fallback recursion
94
- const s = checker.getSymbolAtLocation(decl);
95
- if (s && s !== symbol) {
95
+ const inner = checker.getSymbolAtLocation(decl);
96
+ if (inner && inner !== symbol) {
96
97
  const resolved = resolveFromSymbol(
97
- s,
98
+ inner,
99
+ checker,
100
+ visited,
101
+ depth + 1
102
+ );
103
+ if (resolved) return resolved;
104
+ }
105
+ }
106
+
107
+ // Exports (re-export)
108
+ const exports = (symbol as any).exports;
109
+ if (exports) {
110
+ for (const exp of exports.values()) {
111
+ const resolved = resolveFromSymbol(
112
+ exp,
98
113
  checker,
99
114
  visited,
100
115
  depth + 1
@@ -106,17 +121,19 @@ function resolveFromSymbol(
106
121
  return null;
107
122
  }
108
123
 
124
+ // MAIN FUNCTION
109
125
  export function resolveFunctionFromCall(
110
126
  expr: ts.Expression,
111
127
  checker: ts.TypeChecker
112
128
  ): ts.FunctionLikeDeclaration | null {
113
-
114
- // tracker.track
129
+ // obj.method()
115
130
  if (ts.isPropertyAccessExpression(expr)) {
116
131
  let symbol = checker.getSymbolAtLocation(expr.name);
117
132
 
118
133
  if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
119
- symbol = checker.getAliasedSymbol(symbol);
134
+ try {
135
+ symbol = checker.getAliasedSymbol(symbol);
136
+ } catch {}
120
137
  }
121
138
 
122
139
  if (symbol) {
@@ -125,7 +142,7 @@ export function resolveFunctionFromCall(
125
142
  }
126
143
  }
127
144
 
128
- // tracker["track"]
145
+ // obj["method"]()
129
146
  if (ts.isElementAccessExpression(expr)) {
130
147
  const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
131
148
 
@@ -135,7 +152,7 @@ export function resolveFunctionFromCall(
135
152
  }
136
153
  }
137
154
 
138
- // normal call signatures
155
+ // Call signatures
139
156
  const type = checker.getTypeAtLocation(expr);
140
157
  const signatures = type.getCallSignatures();
141
158
 
@@ -157,7 +174,23 @@ export function resolveFunctionFromCall(
157
174
  }
158
175
  }
159
176
 
160
- // fallback
177
+ // Identifier fallback
178
+ if (ts.isIdentifier(expr)) {
179
+ let symbol = checker.getSymbolAtLocation(expr);
180
+
181
+ if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
182
+ try {
183
+ symbol = checker.getAliasedSymbol(symbol);
184
+ } catch {}
185
+ }
186
+
187
+ if (symbol) {
188
+ const resolved = resolveFromSymbol(symbol, checker);
189
+ if (resolved) return resolved;
190
+ }
191
+ }
192
+
193
+ // LAST fallback
161
194
  const symbol = checker.getSymbolAtLocation(expr);
162
195
  if (symbol) {
163
196
  return resolveFromSymbol(symbol, checker);
@@ -7,9 +7,10 @@ export class TSService {
7
7
  private fileNames = new Set<string>();
8
8
  private service: ts.LanguageService;
9
9
 
10
- private compilerOptions: ts.CompilerOptions;
11
- private baseUrl: string;
12
- private paths: Record<string, string[]>;
10
+ private readonly compilerOptions: ts.CompilerOptions;
11
+ private readonly baseUrl: string;
12
+ private readonly paths: Record<string, string[]>;
13
+
13
14
  private usage = new Map<string, number>();
14
15
  private tick = 0;
15
16
  private MAX_FILES = 2000;
@@ -23,6 +24,7 @@ export class TSService {
23
24
 
24
25
  let compilerOptions: ts.CompilerOptions = {
25
26
  allowJs: true,
27
+ allowNonTsExtensions: true,
26
28
  jsx: ts.JsxEmit.React,
27
29
  jsxImportSource: "react",
28
30
  moduleResolution: ts.ModuleResolutionKind.Node16,
@@ -60,7 +62,17 @@ export class TSService {
60
62
  this.paths = paths;
61
63
 
62
64
  const host: ts.LanguageServiceHost = {
63
- getScriptFileNames: () => Array.from(this.fileNames),
65
+ getScriptFileNames: () => {
66
+ if (this.fileNames.size === 0) {
67
+ return ts.sys.readDirectory(
68
+ this.root,
69
+ [".ts", ".tsx", ".js", ".jsx"],
70
+ undefined,
71
+ undefined
72
+ );
73
+ }
74
+ return Array.from(this.fileNames);
75
+ },
64
76
 
65
77
  getScriptVersion: (file) => {
66
78
  file = this.normalize(file);
@@ -69,13 +81,29 @@ export class TSService {
69
81
 
70
82
  getScriptSnapshot: (file) => {
71
83
  file = this.normalize(file);
72
-
73
84
  const f = this.files.get(file);
74
85
  if (f) {
75
86
  this.touch(file);
76
87
  return ts.ScriptSnapshot.fromString(f.content);
77
88
  }
89
+ if (file.endsWith(".tsx")) {
90
+ const original = file.replace(/\.tsx$/, "");
91
+
92
+ if (fs.existsSync(original)) {
93
+ const content = fs.readFileSync(original, "utf-8");
94
+
95
+ this.files.set(file, {
96
+ content,
97
+ version: 1,
98
+ });
99
+
100
+ this.fileNames.add(file);
101
+ this.touch(file);
102
+ this.evictIfNeeded();
78
103
 
104
+ return ts.ScriptSnapshot.fromString(content);
105
+ }
106
+ }
79
107
  try {
80
108
  if (fs.existsSync(file)) {
81
109
  const content = fs.readFileSync(file, "utf-8");
@@ -93,16 +121,22 @@ export class TSService {
93
121
  }
94
122
  } catch {}
95
123
 
96
- // fallback
97
124
  return ts.ScriptSnapshot.fromString("");
98
125
  },
99
-
100
126
  getCurrentDirectory: () => this.root,
101
127
  getCompilationSettings: () => this.compilerOptions,
102
128
  getDefaultLibFileName: (opts) =>
103
129
  ts.getDefaultLibFilePath(opts),
104
- fileExists: ts.sys.fileExists,
105
- readFile: ts.sys.readFile,
130
+ fileExists: (file) => {
131
+ file = this.normalize(file);
132
+ return this.files.has(file) || ts.sys.fileExists(file);
133
+ },
134
+
135
+ readFile: (file) => {
136
+ file = this.normalize(file);
137
+ return this.files.get(file)?.content ?? ts.sys.readFile(file);
138
+ },
139
+
106
140
  readDirectory: ts.sys.readDirectory,
107
141
  };
108
142
 
@@ -122,7 +156,7 @@ export class TSService {
122
156
  private evictIfNeeded() {
123
157
  if (this.fileNames.size <= this.MAX_FILES) return;
124
158
 
125
- let oldestFile: string | null = null;
159
+ let oldest: string | null = null;
126
160
  let oldestTick = Infinity;
127
161
 
128
162
  for (const f of this.fileNames) {
@@ -130,20 +164,20 @@ export class TSService {
130
164
 
131
165
  if (t < oldestTick) {
132
166
  oldestTick = t;
133
- oldestFile = f;
167
+ oldest = f;
134
168
  }
135
169
  }
136
170
 
137
- if (oldestFile) {
138
- this.files.delete(oldestFile);
139
- this.fileNames.delete(oldestFile);
140
- this.usage.delete(oldestFile);
171
+ if (oldest) {
172
+ this.files.delete(oldest);
173
+ this.fileNames.delete(oldest);
174
+ this.usage.delete(oldest);
141
175
  }
142
176
  }
143
177
 
144
178
  private ensureProgram() {
145
179
  try {
146
- this.service.getProgram();
180
+ this.service.getProgram()?.getTypeChecker();
147
181
  } catch {}
148
182
  }
149
183