@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.
- package/dist/engine/scanner.js +16 -21
- package/dist/utils/universal.js +44 -22
- package/package.json +1 -1
- package/src/engine/scanner.ts +16 -26
- package/src/utils/universal.ts +62 -22
package/dist/engine/scanner.js
CHANGED
|
@@ -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
|
|
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)(
|
|
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
|
-
|
|
91
|
-
(
|
|
92
|
-
|
|
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(
|
|
97
|
-
|
|
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(
|
|
101
|
-
|
|
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
|
-
//
|
|
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)(
|
|
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
|
-
//
|
|
135
|
+
// fallback
|
|
141
136
|
if (!handled) {
|
|
142
137
|
for (const arg of node.arguments) {
|
|
143
|
-
const res = (0, resolver_1.resolveNodeValue)(
|
|
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
|
|
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)
|
package/dist/utils/universal.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
package/src/engine/scanner.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
131
|
-
(
|
|
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(
|
|
139
|
-
|
|
131
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
132
|
+
node.expression.name.text === "track"
|
|
140
133
|
) {
|
|
141
134
|
isTracking = true;
|
|
142
135
|
} else if (
|
|
143
|
-
ts.isIdentifier(
|
|
144
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
223
|
+
// JSX
|
|
234
224
|
if (
|
|
235
225
|
ts.isJsxSelfClosingElement(node) ||
|
|
236
226
|
ts.isJsxOpeningElement(node)
|
package/src/utils/universal.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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))],
|