@eventra_dev/eventra-cli 0.1.27 → 0.1.28

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.
@@ -9,7 +9,6 @@ const resolver_1 = require("./resolver");
9
9
  const callResolver_1 = require("./callResolver");
10
10
  const exportResolver_1 = require("./exportResolver");
11
11
  const functionAnalyzer_1 = require("./functionAnalyzer");
12
- const boundary_1 = require("./boundary");
13
12
  const trackingDetector_1 = require("./trackingDetector");
14
13
  function getCallName(expr) {
15
14
  if (typescript_1.default.isIdentifier(expr))
@@ -70,35 +69,30 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
70
69
  const events = new Set();
71
70
  const detectedFunctionWrappers = new Set();
72
71
  const detectedComponentWrappers = new Map();
73
- if (visited.has(source.fileName) ||
74
- depth > 10 ||
75
- visited.size > 200) {
72
+ if (visited.size > 200 || visited.has(source.fileName)) {
76
73
  return { events, detectedFunctionWrappers, detectedComponentWrappers };
77
74
  }
78
75
  visited.add(source.fileName);
79
76
  function visit(node) {
80
77
  // CALL EXPRESSIONS
81
78
  if (typescript_1.default.isCallExpression(node)) {
82
- const expr = unwrap(node.expression);
83
- const name = getCallName(expr);
79
+ const name = getCallName(node.expression);
84
80
  const isKnownWrapper = config.functionWrappers.some(w => w.name === name);
85
81
  const isTrackingByType = (0, trackingDetector_1.isTrackingCall)(node, checker);
86
- let resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(expr, checker);
82
+ let resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
87
83
  const isAutoWrapper = resolvedFn && (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn) !== null;
88
84
  let isTracking = isKnownWrapper ||
89
85
  isTrackingByType ||
90
- name.startsWith("$") ||
91
- (isAutoWrapper &&
92
- resolvedFn &&
93
- !(0, boundary_1.isExternalFile)(resolvedFn.getSourceFile().fileName));
94
- // fallback
86
+ isAutoWrapper ||
87
+ name.startsWith("$");
88
+ // fallback track(...)
95
89
  if (!isTracking) {
96
- if (typescript_1.default.isPropertyAccessExpression(expr) &&
97
- expr.name.text === "track") {
90
+ if (typescript_1.default.isPropertyAccessExpression(node.expression) &&
91
+ node.expression.name.text === "track") {
98
92
  isTracking = true;
99
93
  }
100
- else if (typescript_1.default.isIdentifier(expr) &&
101
- expr.text === "track") {
94
+ else if (typescript_1.default.isIdentifier(node.expression) &&
95
+ node.expression.text === "track") {
102
96
  isTracking = true;
103
97
  }
104
98
  }
@@ -106,13 +100,14 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
106
100
  typescript_1.default.forEachChild(node, visit);
107
101
  return;
108
102
  }
109
- // detect wrapper
103
+ // DETECT WRAPPERS
110
104
  if (!isKnownWrapper &&
111
105
  isAutoWrapper &&
112
106
  !isTrackingByType &&
113
107
  !name.startsWith("track")) {
114
108
  detectedFunctionWrappers.add(name);
115
109
  }
110
+ // PARAM MAP
116
111
  const paramMap = new Map(inheritedParamMap ?? []);
117
112
  if (resolvedFn && typescript_1.default.isFunctionLike(resolvedFn)) {
118
113
  resolvedFn.parameters.forEach((param, i) => {
@@ -129,7 +124,7 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
129
124
  if (resolvedFn) {
130
125
  const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
131
126
  if (idx !== null && node.arguments[idx]) {
132
- const res = (0, resolver_1.resolveNodeValue)(unwrap(node.arguments[idx]), checker, paramMap);
127
+ const res = (0, resolver_1.resolveNodeValue)(node.arguments[idx], checker, paramMap);
133
128
  res.values.forEach(v => {
134
129
  if (isValidEvent(v))
135
130
  events.add(v);
@@ -137,10 +132,10 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
137
132
  handled = true;
138
133
  }
139
134
  }
140
- // FALLBACK
135
+ // fallback
141
136
  if (!handled) {
142
137
  for (const arg of node.arguments) {
143
- const res = (0, resolver_1.resolveNodeValue)(unwrap(arg), checker, paramMap);
138
+ const res = (0, resolver_1.resolveNodeValue)(arg, checker, paramMap);
144
139
  res.values.forEach(v => {
145
140
  if (isValidEvent(v))
146
141
  events.add(v);
@@ -156,7 +151,7 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
156
151
  }
157
152
  }
158
153
  }
159
- // JSX COMPONENTS
154
+ // JSX
160
155
  if (typescript_1.default.isJsxSelfClosingElement(node) ||
161
156
  typescript_1.default.isJsxOpeningElement(node)) {
162
157
  const tagNode = typescript_1.default.isPropertyAccessExpression(node.tagName)
@@ -5,18 +5,14 @@ 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
- /**
9
- * Strip comments (HTML + JS)
10
- */
8
+ // Strip comments (HTML + JS)
11
9
  function stripComments(input) {
12
10
  return input
13
11
  .replace(/<!--[\s\S]*?-->/g, "")
14
12
  .replace(/\/\*[\s\S]*?\*\//g, "")
15
13
  .replace(/\/\/.*$/gm, "");
16
14
  }
17
- /**
18
- * Extract JS expressions
19
- */
15
+ // Extract JS expressions inside {}
20
16
  function extractJS(input) {
21
17
  const result = [];
22
18
  let i = 0;
@@ -63,9 +59,40 @@ function extractJS(input) {
63
59
  }
64
60
  return result;
65
61
  }
66
- /**
67
- * Loose JS extractor
68
- */
62
+ // Extract inline handlers (onclick="...")
63
+ function extractInlineHandlers(input) {
64
+ const result = [];
65
+ const regex = /\bon\w+\s*=\s*["']([^"']+)["']/g;
66
+ let match;
67
+ while ((match = regex.exec(input))) {
68
+ const code = match[1].trim();
69
+ if (code && /[a-zA-Z0-9_$]\s*\(/.test(code)) {
70
+ result.push(code);
71
+ }
72
+ }
73
+ return result;
74
+ }
75
+ // Extract dynamic imports
76
+ function extractDynamicImports(input, file) {
77
+ const deps = [];
78
+ const calls = [];
79
+ const regex = /import\(\s*["'](.+?)["']\s*\)/g;
80
+ let match;
81
+ while ((match = regex.exec(input))) {
82
+ const mod = match[1];
83
+ const resolved = mod.startsWith("/")
84
+ ? path_1.default.resolve(process.cwd(), "." + mod)
85
+ : path_1.default.resolve(path_1.default.dirname(file), mod);
86
+ deps.push(resolved);
87
+ }
88
+ // also extract calls chained after import
89
+ const callRegex = /import\([^)]+\)\.then\(([^)]+)\)/g;
90
+ while ((match = callRegex.exec(input))) {
91
+ calls.push(match[1]);
92
+ }
93
+ return { deps, calls };
94
+ }
95
+ // Loose JS extractor
69
96
  function extractLooseJS(input) {
70
97
  const result = [];
71
98
  const lines = input.split("\n");
@@ -81,16 +108,7 @@ function extractLooseJS(input) {
81
108
  }
82
109
  return result;
83
110
  }
84
- /**
85
- * UNIVERSAL PARSER
86
- * Support:
87
- * - React / JSX
88
- * - Vue
89
- * - Svelte
90
- * - Astro
91
- * - HTML + JS
92
- * - SSR
93
- */
111
+ // UNIVERSAL PARSER
94
112
  function parseUniversal(content, file) {
95
113
  const parts = [];
96
114
  const deps = [];
@@ -113,15 +131,20 @@ function parseUniversal(content, file) {
113
131
  const scripts = clean.matchAll(/<script\b([^>]*)>([\s\S]*?)<\/script>/g);
114
132
  for (const m of scripts) {
115
133
  const attrs = m[1];
116
- // skip JSON
117
134
  if (/type=["']application\/json/.test(attrs))
118
135
  continue;
119
136
  if (m[2].trim()) {
120
137
  parts.push(m[2]);
121
138
  }
122
139
  }
123
- // REMOVE SCRIPTS → TEMPLATE PART
140
+ // REMOVE scripts
124
141
  const noScripts = clean.replace(/<script[\s\S]*?<\/script>/g, "");
142
+ // INLINE HANDLERS
143
+ parts.push(...extractInlineHandlers(noScripts));
144
+ // DYNAMIC IMPORTS
145
+ const dyn = extractDynamicImports(clean, file);
146
+ deps.push(...dyn.deps);
147
+ parts.push(...dyn.calls);
125
148
  // LOOSE JS
126
149
  parts.push(...extractLooseJS(noScripts));
127
150
  // EXPRESSIONS
@@ -129,7 +152,6 @@ function parseUniversal(content, file) {
129
152
  for (const e of extracted) {
130
153
  parts.push(e + ";");
131
154
  }
132
- // RESULT
133
155
  return {
134
156
  code: [...new Set(parts)].join("\n"),
135
157
  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.27",
3
+ "version": "0.1.28",
4
4
  "description": "Eventra CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -97,11 +97,7 @@ export function scanSource(
97
97
  const detectedFunctionWrappers = new Set<string>();
98
98
  const detectedComponentWrappers = new Map<string, string>();
99
99
 
100
- if (
101
- visited.has(source.fileName) ||
102
- depth > 10 ||
103
- visited.size > 200
104
- ) {
100
+ if (visited.size > 200 || visited.has(source.fileName)) {
105
101
  return { events, detectedFunctionWrappers, detectedComponentWrappers };
106
102
  }
107
103
 
@@ -110,8 +106,7 @@ export function scanSource(
110
106
  function visit(node: ts.Node) {
111
107
  // CALL EXPRESSIONS
112
108
  if (ts.isCallExpression(node)) {
113
- const expr = unwrap(node.expression);
114
- const name = getCallName(expr);
109
+ const name = getCallName(node.expression);
115
110
 
116
111
  const isKnownWrapper = config.functionWrappers.some(
117
112
  w => w.name === name
@@ -119,7 +114,7 @@ export function scanSource(
119
114
 
120
115
  const isTrackingByType = isTrackingCall(node, checker);
121
116
 
122
- let resolvedFn = resolveFunctionFromCall(expr, checker);
117
+ let resolvedFn = resolveFunctionFromCall(node.expression, checker);
123
118
 
124
119
  const isAutoWrapper =
125
120
  resolvedFn && findTrackParamIndex(resolvedFn) !== null;
@@ -127,21 +122,19 @@ export function scanSource(
127
122
  let isTracking =
128
123
  isKnownWrapper ||
129
124
  isTrackingByType ||
130
- name.startsWith("$") ||
131
- (isAutoWrapper &&
132
- resolvedFn &&
133
- !isExternalFile(resolvedFn.getSourceFile().fileName));
125
+ isAutoWrapper ||
126
+ name.startsWith("$");
134
127
 
135
- // fallback
128
+ // fallback track(...)
136
129
  if (!isTracking) {
137
130
  if (
138
- ts.isPropertyAccessExpression(expr) &&
139
- expr.name.text === "track"
131
+ ts.isPropertyAccessExpression(node.expression) &&
132
+ node.expression.name.text === "track"
140
133
  ) {
141
134
  isTracking = true;
142
135
  } else if (
143
- ts.isIdentifier(expr) &&
144
- expr.text === "track"
136
+ ts.isIdentifier(node.expression) &&
137
+ node.expression.text === "track"
145
138
  ) {
146
139
  isTracking = true;
147
140
  }
@@ -152,7 +145,7 @@ export function scanSource(
152
145
  return;
153
146
  }
154
147
 
155
- // detect wrapper
148
+ // DETECT WRAPPERS
156
149
  if (
157
150
  !isKnownWrapper &&
158
151
  isAutoWrapper &&
@@ -162,6 +155,7 @@ export function scanSource(
162
155
  detectedFunctionWrappers.add(name);
163
156
  }
164
157
 
158
+ // PARAM MAP
165
159
  const paramMap = new Map(inheritedParamMap ?? []);
166
160
 
167
161
  if (resolvedFn && ts.isFunctionLike(resolvedFn)) {
@@ -183,7 +177,7 @@ export function scanSource(
183
177
 
184
178
  if (idx !== null && node.arguments[idx]) {
185
179
  const res = resolveNodeValue(
186
- unwrap(node.arguments[idx]),
180
+ node.arguments[idx],
187
181
  checker,
188
182
  paramMap
189
183
  );
@@ -196,14 +190,10 @@ export function scanSource(
196
190
  }
197
191
  }
198
192
 
199
- // FALLBACK
193
+ // fallback
200
194
  if (!handled) {
201
195
  for (const arg of node.arguments) {
202
- const res = resolveNodeValue(
203
- unwrap(arg),
204
- checker,
205
- paramMap
206
- );
196
+ const res = resolveNodeValue(arg, checker, paramMap);
207
197
 
208
198
  res.values.forEach(v => {
209
199
  if (isValidEvent(v)) events.add(v);
@@ -230,7 +220,7 @@ export function scanSource(
230
220
  }
231
221
  }
232
222
 
233
- // JSX COMPONENTS
223
+ // JSX
234
224
  if (
235
225
  ts.isJsxSelfClosingElement(node) ||
236
226
  ts.isJsxOpeningElement(node)
@@ -1,9 +1,7 @@
1
1
  import path from "path";
2
2
  import { ParseResult } from "../types";
3
3
 
4
- /**
5
- * Strip comments (HTML + JS)
6
- */
4
+ // Strip comments (HTML + JS)
7
5
  function stripComments(input: string): string {
8
6
  return input
9
7
  .replace(/<!--[\s\S]*?-->/g, "")
@@ -11,9 +9,7 @@ function stripComments(input: string): string {
11
9
  .replace(/\/\/.*$/gm, "");
12
10
  }
13
11
 
14
- /**
15
- * Extract JS expressions
16
- */
12
+ // Extract JS expressions inside {}
17
13
  function extractJS(input: string): string[] {
18
14
  const result: string[] = [];
19
15
 
@@ -68,9 +64,56 @@ function extractJS(input: string): string[] {
68
64
  return result;
69
65
  }
70
66
 
71
- /**
72
- * Loose JS extractor
73
- */
67
+ // Extract inline handlers (onclick="...")
68
+ function extractInlineHandlers(input: string): string[] {
69
+ const result: string[] = [];
70
+
71
+ const regex = /\bon\w+\s*=\s*["']([^"']+)["']/g;
72
+
73
+ let match;
74
+ while ((match = regex.exec(input))) {
75
+ const code = match[1].trim();
76
+
77
+ if (code && /[a-zA-Z0-9_$]\s*\(/.test(code)) {
78
+ result.push(code);
79
+ }
80
+ }
81
+
82
+ return result;
83
+ }
84
+
85
+ // Extract dynamic imports
86
+ function extractDynamicImports(
87
+ input: string,
88
+ file: string
89
+ ): { deps: string[]; calls: string[] } {
90
+ const deps: string[] = [];
91
+ const calls: string[] = [];
92
+
93
+ const regex = /import\(\s*["'](.+?)["']\s*\)/g;
94
+
95
+ let match;
96
+ while ((match = regex.exec(input))) {
97
+ const mod = match[1];
98
+
99
+ const resolved = mod.startsWith("/")
100
+ ? path.resolve(process.cwd(), "." + mod)
101
+ : path.resolve(path.dirname(file), mod);
102
+
103
+ deps.push(resolved);
104
+ }
105
+
106
+ // also extract calls chained after import
107
+ const callRegex = /import\([^)]+\)\.then\(([^)]+)\)/g;
108
+
109
+ while ((match = callRegex.exec(input))) {
110
+ calls.push(match[1]);
111
+ }
112
+
113
+ return { deps, calls };
114
+ }
115
+
116
+ // Loose JS extractor
74
117
  function extractLooseJS(input: string): string[] {
75
118
  const result: string[] = [];
76
119
 
@@ -90,16 +133,7 @@ function extractLooseJS(input: string): string[] {
90
133
  return result;
91
134
  }
92
135
 
93
- /**
94
- * UNIVERSAL PARSER
95
- * Support:
96
- * - React / JSX
97
- * - Vue
98
- * - Svelte
99
- * - Astro
100
- * - HTML + JS
101
- * - SSR
102
- */
136
+ // UNIVERSAL PARSER
103
137
  export function parseUniversal(
104
138
  content: string,
105
139
  file: string
@@ -138,7 +172,6 @@ export function parseUniversal(
138
172
  for (const m of scripts) {
139
173
  const attrs = m[1];
140
174
 
141
- // skip JSON
142
175
  if (/type=["']application\/json/.test(attrs)) continue;
143
176
 
144
177
  if (m[2].trim()) {
@@ -146,12 +179,20 @@ export function parseUniversal(
146
179
  }
147
180
  }
148
181
 
149
- // REMOVE SCRIPTS → TEMPLATE PART
182
+ // REMOVE scripts
150
183
  const noScripts = clean.replace(
151
184
  /<script[\s\S]*?<\/script>/g,
152
185
  ""
153
186
  );
154
187
 
188
+ // INLINE HANDLERS
189
+ parts.push(...extractInlineHandlers(noScripts));
190
+
191
+ // DYNAMIC IMPORTS
192
+ const dyn = extractDynamicImports(clean, file);
193
+ deps.push(...dyn.deps);
194
+ parts.push(...dyn.calls);
195
+
155
196
  // LOOSE JS
156
197
  parts.push(...extractLooseJS(noScripts));
157
198
 
@@ -161,7 +202,6 @@ export function parseUniversal(
161
202
  parts.push(e + ";");
162
203
  }
163
204
 
164
- // RESULT
165
205
  return {
166
206
  code: [...new Set(parts)].join("\n"),
167
207
  deps: [...new Set(deps.filter(Boolean))],