@eventra_dev/eventra-cli 0.1.31 → 0.1.32
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/callResolver.js +56 -25
- package/dist/engine/languageService.js +36 -11
- package/dist/engine/scanner.js +74 -71
- package/dist/utils/universal.js +73 -21
- package/package.json +1 -1
- package/src/engine/callResolver.ts +61 -28
- package/src/engine/languageService.ts +50 -16
- package/src/engine/scanner.ts +83 -94
- package/src/utils/universal.ts +90 -21
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.resolveFunctionFromCall = resolveFunctionFromCall;
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
// HELPERS
|
|
8
9
|
function isFunctionLike(node) {
|
|
9
10
|
return (typescript_1.default.isFunctionDeclaration(node) ||
|
|
10
11
|
typescript_1.default.isMethodDeclaration(node) ||
|
|
@@ -12,32 +13,33 @@ function isFunctionLike(node) {
|
|
|
12
13
|
typescript_1.default.isFunctionExpression(node) ||
|
|
13
14
|
typescript_1.default.isArrowFunction(node));
|
|
14
15
|
}
|
|
16
|
+
// symbol → function
|
|
15
17
|
function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
|
|
18
|
+
if (!symbol)
|
|
19
|
+
return null;
|
|
16
20
|
if (visited.has(symbol))
|
|
17
21
|
return null;
|
|
18
|
-
if (depth >
|
|
22
|
+
if (depth > 10)
|
|
19
23
|
return null;
|
|
20
24
|
visited.add(symbol);
|
|
21
|
-
// unwrap
|
|
25
|
+
// Alias unwrap (import/export)
|
|
22
26
|
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// re-export support
|
|
27
|
-
if (symbol.exports) {
|
|
28
|
-
for (const exp of symbol.exports.values()) {
|
|
29
|
-
const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
|
|
30
|
-
if (resolved)
|
|
31
|
-
return resolved;
|
|
27
|
+
try {
|
|
28
|
+
const aliased = checker.getAliasedSymbol(symbol);
|
|
29
|
+
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
32
30
|
}
|
|
31
|
+
catch { }
|
|
33
32
|
}
|
|
33
|
+
// Declarations
|
|
34
34
|
const decls = symbol.getDeclarations() ?? [];
|
|
35
35
|
for (const decl of decls) {
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* direct function
|
|
38
|
+
*/
|
|
37
39
|
if (isFunctionLike(decl)) {
|
|
38
40
|
return decl;
|
|
39
41
|
}
|
|
40
|
-
// VARIABLE → FUNCTION
|
|
42
|
+
// VARIABLE → FUNCTION const fn = () => {}
|
|
41
43
|
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
42
44
|
if (isFunctionLike(decl.initializer)) {
|
|
43
45
|
return decl.initializer;
|
|
@@ -49,17 +51,18 @@ function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
|
|
|
49
51
|
return resolved;
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
//
|
|
54
|
+
// CLASS METHOD
|
|
55
|
+
if (typescript_1.default.isMethodDeclaration(decl)) {
|
|
56
|
+
return decl;
|
|
57
|
+
}
|
|
58
|
+
// interface / type method
|
|
53
59
|
if (typescript_1.default.isMethodSignature(decl)) {
|
|
54
60
|
const parent = decl.parent;
|
|
55
61
|
if (typescript_1.default.isInterfaceDeclaration(parent) ||
|
|
56
62
|
typescript_1.default.isTypeLiteralNode(parent)) {
|
|
57
|
-
const name = decl.name.getText();
|
|
58
63
|
const type = checker.getTypeAtLocation(parent);
|
|
59
64
|
const props = type.getProperties();
|
|
60
65
|
for (const prop of props) {
|
|
61
|
-
if (prop.name !== name)
|
|
62
|
-
continue;
|
|
63
66
|
const resolved = resolveFromSymbol(prop, checker, visited, depth + 1);
|
|
64
67
|
if (resolved)
|
|
65
68
|
return resolved;
|
|
@@ -67,21 +70,34 @@ function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
|
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
// fallback recursion
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
const resolved = resolveFromSymbol(
|
|
73
|
+
const inner = checker.getSymbolAtLocation(decl);
|
|
74
|
+
if (inner && inner !== symbol) {
|
|
75
|
+
const resolved = resolveFromSymbol(inner, checker, visited, depth + 1);
|
|
76
|
+
if (resolved)
|
|
77
|
+
return resolved;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Exports (re-export)
|
|
81
|
+
const exports = symbol.exports;
|
|
82
|
+
if (exports) {
|
|
83
|
+
for (const exp of exports.values()) {
|
|
84
|
+
const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
|
|
73
85
|
if (resolved)
|
|
74
86
|
return resolved;
|
|
75
87
|
}
|
|
76
88
|
}
|
|
77
89
|
return null;
|
|
78
90
|
}
|
|
91
|
+
// MAIN FUNCTION
|
|
79
92
|
function resolveFunctionFromCall(expr, checker) {
|
|
80
|
-
//
|
|
93
|
+
// obj.method()
|
|
81
94
|
if (typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
82
95
|
let symbol = checker.getSymbolAtLocation(expr.name);
|
|
83
96
|
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
84
|
-
|
|
97
|
+
try {
|
|
98
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
85
101
|
}
|
|
86
102
|
if (symbol) {
|
|
87
103
|
const resolved = resolveFromSymbol(symbol, checker);
|
|
@@ -89,7 +105,7 @@ function resolveFunctionFromCall(expr, checker) {
|
|
|
89
105
|
return resolved;
|
|
90
106
|
}
|
|
91
107
|
}
|
|
92
|
-
//
|
|
108
|
+
// obj["method"]()
|
|
93
109
|
if (typescript_1.default.isElementAccessExpression(expr)) {
|
|
94
110
|
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
95
111
|
if (symbol) {
|
|
@@ -98,7 +114,7 @@ function resolveFunctionFromCall(expr, checker) {
|
|
|
98
114
|
return resolved;
|
|
99
115
|
}
|
|
100
116
|
}
|
|
101
|
-
//
|
|
117
|
+
// Call signatures
|
|
102
118
|
const type = checker.getTypeAtLocation(expr);
|
|
103
119
|
const signatures = type.getCallSignatures();
|
|
104
120
|
for (const sig of signatures) {
|
|
@@ -117,7 +133,22 @@ function resolveFunctionFromCall(expr, checker) {
|
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
|
-
// fallback
|
|
136
|
+
// Identifier fallback
|
|
137
|
+
if (typescript_1.default.isIdentifier(expr)) {
|
|
138
|
+
let symbol = checker.getSymbolAtLocation(expr);
|
|
139
|
+
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
140
|
+
try {
|
|
141
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
142
|
+
}
|
|
143
|
+
catch { }
|
|
144
|
+
}
|
|
145
|
+
if (symbol) {
|
|
146
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
147
|
+
if (resolved)
|
|
148
|
+
return resolved;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// LAST fallback
|
|
121
152
|
const symbol = checker.getSymbolAtLocation(expr);
|
|
122
153
|
if (symbol) {
|
|
123
154
|
return resolveFromSymbol(symbol, checker);
|
|
@@ -23,6 +23,7 @@ class TSService {
|
|
|
23
23
|
const configPath = typescript_1.default.findConfigFile(root, typescript_1.default.sys.fileExists, "tsconfig.json");
|
|
24
24
|
let compilerOptions = {
|
|
25
25
|
allowJs: true,
|
|
26
|
+
allowNonTsExtensions: true,
|
|
26
27
|
jsx: typescript_1.default.JsxEmit.React,
|
|
27
28
|
jsxImportSource: "react",
|
|
28
29
|
moduleResolution: typescript_1.default.ModuleResolutionKind.Node16,
|
|
@@ -49,7 +50,12 @@ class TSService {
|
|
|
49
50
|
this.baseUrl = baseUrl;
|
|
50
51
|
this.paths = paths;
|
|
51
52
|
const host = {
|
|
52
|
-
getScriptFileNames: () =>
|
|
53
|
+
getScriptFileNames: () => {
|
|
54
|
+
if (this.fileNames.size === 0) {
|
|
55
|
+
return typescript_1.default.sys.readDirectory(this.root, [".ts", ".tsx", ".js", ".jsx"], undefined, undefined);
|
|
56
|
+
}
|
|
57
|
+
return Array.from(this.fileNames);
|
|
58
|
+
},
|
|
53
59
|
getScriptVersion: (file) => {
|
|
54
60
|
file = this.normalize(file);
|
|
55
61
|
return this.files.get(file)?.version.toString() ?? "0";
|
|
@@ -61,6 +67,20 @@ class TSService {
|
|
|
61
67
|
this.touch(file);
|
|
62
68
|
return typescript_1.default.ScriptSnapshot.fromString(f.content);
|
|
63
69
|
}
|
|
70
|
+
if (file.endsWith(".tsx")) {
|
|
71
|
+
const original = file.replace(/\.tsx$/, "");
|
|
72
|
+
if (fs_1.default.existsSync(original)) {
|
|
73
|
+
const content = fs_1.default.readFileSync(original, "utf-8");
|
|
74
|
+
this.files.set(file, {
|
|
75
|
+
content,
|
|
76
|
+
version: 1,
|
|
77
|
+
});
|
|
78
|
+
this.fileNames.add(file);
|
|
79
|
+
this.touch(file);
|
|
80
|
+
this.evictIfNeeded();
|
|
81
|
+
return typescript_1.default.ScriptSnapshot.fromString(content);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
64
84
|
try {
|
|
65
85
|
if (fs_1.default.existsSync(file)) {
|
|
66
86
|
const content = fs_1.default.readFileSync(file, "utf-8");
|
|
@@ -75,14 +95,19 @@ class TSService {
|
|
|
75
95
|
}
|
|
76
96
|
}
|
|
77
97
|
catch { }
|
|
78
|
-
// fallback
|
|
79
98
|
return typescript_1.default.ScriptSnapshot.fromString("");
|
|
80
99
|
},
|
|
81
100
|
getCurrentDirectory: () => this.root,
|
|
82
101
|
getCompilationSettings: () => this.compilerOptions,
|
|
83
102
|
getDefaultLibFileName: (opts) => typescript_1.default.getDefaultLibFilePath(opts),
|
|
84
|
-
fileExists:
|
|
85
|
-
|
|
103
|
+
fileExists: (file) => {
|
|
104
|
+
file = this.normalize(file);
|
|
105
|
+
return this.files.has(file) || typescript_1.default.sys.fileExists(file);
|
|
106
|
+
},
|
|
107
|
+
readFile: (file) => {
|
|
108
|
+
file = this.normalize(file);
|
|
109
|
+
return this.files.get(file)?.content ?? typescript_1.default.sys.readFile(file);
|
|
110
|
+
},
|
|
86
111
|
readDirectory: typescript_1.default.sys.readDirectory,
|
|
87
112
|
};
|
|
88
113
|
this.service = typescript_1.default.createLanguageService(host);
|
|
@@ -98,24 +123,24 @@ class TSService {
|
|
|
98
123
|
evictIfNeeded() {
|
|
99
124
|
if (this.fileNames.size <= this.MAX_FILES)
|
|
100
125
|
return;
|
|
101
|
-
let
|
|
126
|
+
let oldest = null;
|
|
102
127
|
let oldestTick = Infinity;
|
|
103
128
|
for (const f of this.fileNames) {
|
|
104
129
|
const t = this.usage.get(f) ?? 0;
|
|
105
130
|
if (t < oldestTick) {
|
|
106
131
|
oldestTick = t;
|
|
107
|
-
|
|
132
|
+
oldest = f;
|
|
108
133
|
}
|
|
109
134
|
}
|
|
110
|
-
if (
|
|
111
|
-
this.files.delete(
|
|
112
|
-
this.fileNames.delete(
|
|
113
|
-
this.usage.delete(
|
|
135
|
+
if (oldest) {
|
|
136
|
+
this.files.delete(oldest);
|
|
137
|
+
this.fileNames.delete(oldest);
|
|
138
|
+
this.usage.delete(oldest);
|
|
114
139
|
}
|
|
115
140
|
}
|
|
116
141
|
ensureProgram() {
|
|
117
142
|
try {
|
|
118
|
-
this.service.getProgram();
|
|
143
|
+
this.service.getProgram()?.getTypeChecker();
|
|
119
144
|
}
|
|
120
145
|
catch { }
|
|
121
146
|
}
|
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 functionAnalyzer_1 = require("./functionAnalyzer");
|
|
11
11
|
const exportResolver_1 = require("./exportResolver");
|
|
12
|
-
const boundary_1 = require("./boundary");
|
|
13
12
|
// UTILS
|
|
14
13
|
function getJsxAttrName(name) {
|
|
15
14
|
if (typescript_1.default.isIdentifier(name))
|
|
@@ -136,67 +135,58 @@ function findTrackedPropName(fn, checker) {
|
|
|
136
135
|
return result;
|
|
137
136
|
}
|
|
138
137
|
// MAIN SCANNER
|
|
139
|
-
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap, wrapperCache) {
|
|
140
|
-
wrapperCache = wrapperCache ?? new Map();
|
|
138
|
+
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap, wrapperCache = new Map()) {
|
|
141
139
|
const events = new Set();
|
|
142
140
|
const detectedFunctionWrappers = new Set();
|
|
143
141
|
const detectedComponentWrappers = new Map();
|
|
144
|
-
if (visited.
|
|
142
|
+
if (visited.has(source.fileName) || visited.size > 200) {
|
|
145
143
|
return { events, detectedFunctionWrappers, detectedComponentWrappers };
|
|
146
144
|
}
|
|
147
145
|
visited.add(source.fileName);
|
|
148
146
|
function visit(node) {
|
|
149
147
|
// JSX WRAPPERS
|
|
150
|
-
if (typescript_1.default.isJsxSelfClosingElement(node) ||
|
|
151
|
-
typescript_1.default.isJsxOpeningElement(node)) {
|
|
148
|
+
if (typescript_1.default.isJsxSelfClosingElement(node) || typescript_1.default.isJsxOpeningElement(node)) {
|
|
152
149
|
const tagName = getJsxTagName(node.tagName);
|
|
153
150
|
// skip html
|
|
154
|
-
if (
|
|
151
|
+
if (tagName[0] === tagName[0].toLowerCase()) {
|
|
155
152
|
typescript_1.default.forEachChild(node, visit);
|
|
156
153
|
return;
|
|
157
154
|
}
|
|
158
|
-
let wrapper = config.wrappers.find(
|
|
155
|
+
let wrapper = config.wrappers.find(w => w.name === tagName);
|
|
156
|
+
// AUTO DETECT
|
|
159
157
|
if (!wrapper) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
164
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
165
|
-
}
|
|
166
|
-
symbol =
|
|
167
|
-
(0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
168
|
-
cacheKey = checker.getFullyQualifiedName(symbol);
|
|
169
|
-
}
|
|
170
|
-
if (wrapperCache?.has(cacheKey)) {
|
|
171
|
-
const cached = wrapperCache.get(cacheKey);
|
|
172
|
-
if (cached) {
|
|
158
|
+
if (wrapperCache.has(tagName)) {
|
|
159
|
+
const cached = wrapperCache.get(tagName);
|
|
160
|
+
if (cached)
|
|
173
161
|
wrapper = { name: tagName, prop: cached };
|
|
174
|
-
}
|
|
175
162
|
}
|
|
176
|
-
else
|
|
177
|
-
let
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (typescript_1.default.isFunctionDeclaration(decl))
|
|
181
|
-
fn = decl;
|
|
182
|
-
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
183
|
-
decl.initializer &&
|
|
184
|
-
(typescript_1.default.isArrowFunction(decl.initializer) ||
|
|
185
|
-
typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
186
|
-
fn = decl.initializer;
|
|
187
|
-
}
|
|
188
|
-
if (!fn)
|
|
189
|
-
continue;
|
|
190
|
-
const prop = findTrackedPropName(fn, checker);
|
|
191
|
-
if (prop) {
|
|
192
|
-
detected = prop;
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
163
|
+
else {
|
|
164
|
+
let symbol = checker.getSymbolAtLocation(node.tagName);
|
|
165
|
+
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
166
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
195
167
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
168
|
+
if (symbol) {
|
|
169
|
+
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
170
|
+
for (const decl of symbol.getDeclarations() ?? []) {
|
|
171
|
+
let fn = null;
|
|
172
|
+
if (typescript_1.default.isFunctionDeclaration(decl))
|
|
173
|
+
fn = decl;
|
|
174
|
+
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
175
|
+
decl.initializer &&
|
|
176
|
+
(typescript_1.default.isArrowFunction(decl.initializer) ||
|
|
177
|
+
typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
178
|
+
fn = decl.initializer;
|
|
179
|
+
}
|
|
180
|
+
if (!fn)
|
|
181
|
+
continue;
|
|
182
|
+
const prop = findTrackedPropName(fn, checker);
|
|
183
|
+
wrapperCache.set(tagName, prop ?? null);
|
|
184
|
+
if (prop) {
|
|
185
|
+
wrapper = { name: tagName, prop };
|
|
186
|
+
detectedComponentWrappers.set(tagName, prop);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
200
190
|
}
|
|
201
191
|
}
|
|
202
192
|
}
|
|
@@ -210,16 +200,16 @@ function scanSource(source, checker, config, visited = new Set(), inheritedParam
|
|
|
210
200
|
if (!attr.initializer)
|
|
211
201
|
continue;
|
|
212
202
|
if (typescript_1.default.isStringLiteral(attr.initializer)) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
const v = attr.initializer.text;
|
|
204
|
+
if (isValidEvent(v))
|
|
205
|
+
events.add(v);
|
|
216
206
|
}
|
|
217
207
|
if (typescript_1.default.isJsxExpression(attr.initializer)) {
|
|
218
208
|
const expr = attr.initializer.expression;
|
|
219
209
|
if (!expr)
|
|
220
210
|
continue;
|
|
221
211
|
const res = (0, resolver_1.resolveNodeValue)(expr, checker, inheritedParamMap);
|
|
222
|
-
res.values.forEach(
|
|
212
|
+
res.values.forEach(v => {
|
|
223
213
|
if (isValidEvent(v))
|
|
224
214
|
events.add(v);
|
|
225
215
|
});
|
|
@@ -229,24 +219,39 @@ function scanSource(source, checker, config, visited = new Set(), inheritedParam
|
|
|
229
219
|
typescript_1.default.forEachChild(node, visit);
|
|
230
220
|
return;
|
|
231
221
|
}
|
|
232
|
-
// FUNCTION
|
|
222
|
+
// FUNCTION CALLS
|
|
233
223
|
if (typescript_1.default.isCallExpression(node)) {
|
|
234
|
-
|
|
235
|
-
|
|
224
|
+
let trackParamIndex = null;
|
|
225
|
+
let isTracking = false;
|
|
226
|
+
const callName = getCallName(node.expression);
|
|
227
|
+
// direct .track()
|
|
228
|
+
if (typescript_1.default.isPropertyAccessExpression(node.expression) &&
|
|
229
|
+
node.expression.name.text === "track") {
|
|
230
|
+
isTracking = true;
|
|
231
|
+
trackParamIndex = 0;
|
|
232
|
+
}
|
|
233
|
+
// config wrapper
|
|
234
|
+
const manualWrapper = config.functionWrappers.find(w => w.name === callName);
|
|
235
|
+
if (manualWrapper) {
|
|
236
|
+
isTracking = true;
|
|
237
|
+
trackParamIndex = 0;
|
|
238
|
+
}
|
|
239
|
+
// resolve function → check body
|
|
236
240
|
const resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
!
|
|
241
|
+
if (resolvedFn) {
|
|
242
|
+
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
|
|
243
|
+
if (idx !== null) {
|
|
244
|
+
isTracking = true;
|
|
245
|
+
trackParamIndex = idx;
|
|
246
|
+
if (!manualWrapper && callName !== "track") {
|
|
247
|
+
detectedFunctionWrappers.add(callName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
243
251
|
if (!isTracking) {
|
|
244
252
|
typescript_1.default.forEachChild(node, visit);
|
|
245
253
|
return;
|
|
246
254
|
}
|
|
247
|
-
if (!isKnownWrapper && isAutoWrapper && name !== "track") {
|
|
248
|
-
detectedFunctionWrappers.add(name);
|
|
249
|
-
}
|
|
250
255
|
const paramMap = new Map(inheritedParamMap ?? []);
|
|
251
256
|
if (resolvedFn && typescript_1.default.isFunctionLike(resolvedFn)) {
|
|
252
257
|
resolvedFn.parameters.forEach((param, i) => {
|
|
@@ -258,21 +263,19 @@ function scanSource(source, checker, config, visited = new Set(), inheritedParam
|
|
|
258
263
|
}
|
|
259
264
|
});
|
|
260
265
|
}
|
|
261
|
-
if (
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
events.add(v);
|
|
268
|
-
});
|
|
269
|
-
}
|
|
266
|
+
if (trackParamIndex !== null && node.arguments[trackParamIndex]) {
|
|
267
|
+
const res = (0, resolver_1.resolveNodeValue)(node.arguments[trackParamIndex], checker, paramMap);
|
|
268
|
+
res.values.forEach(v => {
|
|
269
|
+
if (isValidEvent(v))
|
|
270
|
+
events.add(v);
|
|
271
|
+
});
|
|
270
272
|
}
|
|
273
|
+
// CROSS FILE
|
|
271
274
|
if (resolvedFn) {
|
|
272
275
|
const sf = resolvedFn.getSourceFile();
|
|
273
276
|
if (!visited.has(sf.fileName)) {
|
|
274
277
|
const res = scanSource(sf, checker, config, visited, paramMap, wrapperCache);
|
|
275
|
-
res.events.forEach(
|
|
278
|
+
res.events.forEach(e => events.add(e));
|
|
276
279
|
}
|
|
277
280
|
}
|
|
278
281
|
}
|
package/dist/utils/universal.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
16
|
+
/* ---------------------------------- */
|
|
17
|
+
/* TRACK DETECTOR */
|
|
18
|
+
/* ---------------------------------- */
|
|
15
19
|
function looksLikeTracking(code) {
|
|
16
|
-
return /\btrack\
|
|
20
|
+
return /\btrack\s*\(|\.track\s*\(/.test(code);
|
|
17
21
|
}
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
+
/* ---------------------------------- */
|
|
88
|
+
/* SVELTE HANDLERS */
|
|
89
|
+
/* ---------------------------------- */
|
|
66
90
|
function extractSvelteHandlers(input) {
|
|
67
91
|
const result = [];
|
|
68
|
-
const regex = /\bon:[\w]+\s*=\s*{([
|
|
92
|
+
const regex = /\bon:[\w]+\s*=\s*{([\s\S]*?)}/g;
|
|
69
93
|
for (const m of input.matchAll(regex)) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const expr = m[1];
|
|
95
|
+
const nested = extractBracedExpressions(`{${expr}}`);
|
|
96
|
+
result.push(...nested);
|
|
73
97
|
}
|
|
74
98
|
return result;
|
|
75
99
|
}
|
|
76
|
-
|
|
100
|
+
/* ---------------------------------- */
|
|
101
|
+
/* SCRIPT EXTRACTION */
|
|
102
|
+
/* ---------------------------------- */
|
|
77
103
|
function extractScripts(input, file) {
|
|
78
104
|
const scripts = [];
|
|
79
105
|
const deps = [];
|
|
80
|
-
// src
|
|
81
|
-
const srcRegex = /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/
|
|
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
|
|
90
|
-
const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(...
|
|
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))],
|