@eventra_dev/eventra-cli 0.1.1 → 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.
- package/dist/engine/callResolver.js +22 -19
- package/dist/engine/engine.js +20 -6
- package/dist/engine/graph.js +11 -8
- package/dist/engine/hash.js +10 -0
- package/dist/engine/languageService.js +67 -0
- package/dist/engine/resolver.js +56 -25
- package/dist/engine/scanner.js +39 -67
- package/package.json +1 -1
- package/src/engine/callResolver.ts +25 -19
- package/src/engine/engine.ts +27 -6
- package/src/engine/graph.ts +10 -10
- package/src/engine/hash.ts +5 -0
- package/src/engine/languageService.ts +73 -0
- package/src/engine/resolver.ts +59 -26
- package/src/engine/scanner.ts +39 -68
- package/src/types.ts +5 -0
- package/dist/engine/extract.js +0 -53
- package/dist/engine/flow.js +0 -38
- package/dist/engine/project.js +0 -74
- package/dist/engine/symbol.js +0 -31
- package/src/engine/extract.ts +0 -74
- package/src/engine/flow.ts +0 -54
- package/src/engine/project.ts +0 -75
- package/src/engine/symbol.ts +0 -42
package/src/engine/resolver.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from "typescript";
|
|
2
2
|
|
|
3
3
|
export type ResolveResult = {
|
|
4
4
|
values: string[];
|
|
@@ -6,45 +6,78 @@ export type ResolveResult = {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export function resolveNodeValue(
|
|
9
|
-
node: Node,
|
|
9
|
+
node: ts.Node,
|
|
10
10
|
checker: ts.TypeChecker
|
|
11
11
|
): ResolveResult | null {
|
|
12
|
-
if (!Node.isIdentifier(node)) return null;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (ts.isStringLiteral(node)) {
|
|
14
|
+
return { values: [node.text], dynamic: false };
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
if (ts.isTemplateExpression(node)) {
|
|
18
|
+
let result = node.head.text;
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
for (const span of node.templateSpans) {
|
|
21
|
+
result += "*";
|
|
22
|
+
result += span.literal.text;
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
25
|
+
return { values: [result], dynamic: true };
|
|
26
|
+
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (ts.isConditionalExpression(node)) {
|
|
29
|
+
const a = resolveNodeValue(node.whenTrue, checker);
|
|
30
|
+
const b = resolveNodeValue(node.whenFalse, checker);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
return {
|
|
33
|
+
values: [...new Set([...(a?.values ?? []), ...(b?.values ?? [])])],
|
|
34
|
+
dynamic: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
if (ts.isIdentifier(node)) {
|
|
39
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
40
|
+
if (!symbol) return null;
|
|
41
|
+
|
|
42
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
43
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
44
|
+
}
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
for (const decl of symbol.getDeclarations() ?? []) {
|
|
47
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
48
|
+
return resolveNodeValue(decl.initializer, checker);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (ts.isEnumMember(decl) && decl.initializer) {
|
|
52
|
+
if (ts.isStringLiteral(decl.initializer)) {
|
|
53
|
+
return {
|
|
54
|
+
values: [decl.initializer.text],
|
|
55
|
+
dynamic: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
40
58
|
}
|
|
41
59
|
}
|
|
60
|
+
}
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
|
|
62
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
63
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
if (symbol) {
|
|
66
|
+
let s = symbol;
|
|
67
|
+
|
|
68
|
+
if (s.flags & ts.SymbolFlags.Alias) {
|
|
69
|
+
s = checker.getAliasedSymbol(s);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const decl of s.getDeclarations() ?? []) {
|
|
73
|
+
if (ts.isEnumMember(decl) && decl.initializer) {
|
|
74
|
+
if (ts.isStringLiteral(decl.initializer)) {
|
|
75
|
+
return {
|
|
76
|
+
values: [decl.initializer.text],
|
|
77
|
+
dynamic: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
48
81
|
}
|
|
49
82
|
}
|
|
50
83
|
}
|
package/src/engine/scanner.ts
CHANGED
|
@@ -1,90 +1,61 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { resolveNodeValue } from "./resolver";
|
|
3
3
|
import { resolveFunctionFromCall } from "./callResolver";
|
|
4
|
-
import { getForwardedParamIndex } from "./flow";
|
|
5
|
-
|
|
6
|
-
function getCallName(expr: Node): string {
|
|
7
|
-
if (Node.isIdentifier(expr)) return expr.getText();
|
|
8
|
-
if (Node.isPropertyAccessExpression(expr)) return expr.getName();
|
|
9
|
-
return expr.getText();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function isTrackLike(name: string) {
|
|
13
|
-
return /(track|trackFeature)/i.test(name);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function pickArg(args: Node[]) {
|
|
17
|
-
return (
|
|
18
|
-
args.find(Node.isStringLiteral) ??
|
|
19
|
-
args.find(Node.isTemplateExpression) ??
|
|
20
|
-
args.find(Node.isObjectLiteralExpression) ??
|
|
21
|
-
args[0]
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
4
|
|
|
25
5
|
export function scanSource(
|
|
26
|
-
source: SourceFile,
|
|
27
|
-
checker: ts.TypeChecker
|
|
6
|
+
source: ts.SourceFile,
|
|
7
|
+
checker: ts.TypeChecker,
|
|
8
|
+
visited = new Set<string>()
|
|
28
9
|
) {
|
|
29
10
|
const events = new Set<string>();
|
|
30
11
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
if (visited.has(source.fileName)) return events;
|
|
13
|
+
visited.add(source.fileName);
|
|
14
|
+
|
|
15
|
+
function visit(node: ts.Node) {
|
|
16
|
+
if (ts.isCallExpression(node)) {
|
|
17
|
+
const name = getCallName(node.expression);
|
|
35
18
|
|
|
36
|
-
|
|
19
|
+
if (/(track|event|capture|send)/i.test(name)) {
|
|
20
|
+
let target = node.arguments[0];
|
|
37
21
|
|
|
38
|
-
|
|
39
|
-
if (!args.length) continue;
|
|
22
|
+
const fn = resolveFunctionFromCall(node.expression, checker);
|
|
40
23
|
|
|
41
|
-
|
|
24
|
+
// 🔥 cross-file
|
|
25
|
+
if (fn) {
|
|
26
|
+
const sf = fn.getSourceFile();
|
|
42
27
|
|
|
43
|
-
|
|
28
|
+
if (!visited.has(sf.fileName)) {
|
|
29
|
+
scanSource(sf, checker, visited).forEach(e => events.add(e));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
if (target) {
|
|
34
|
+
const res = resolveNodeValue(target, checker);
|
|
35
|
+
|
|
36
|
+
res?.values.forEach(v => {
|
|
37
|
+
if (v && v.length > 1 && !v.includes(" ")) {
|
|
38
|
+
events.add(v);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
if (isValid(value)) events.add(value);
|
|
54
|
-
});
|
|
45
|
+
ts.forEachChild(node, visit);
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
const jsx = [
|
|
59
|
-
...source.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
|
|
60
|
-
...source.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
for (const el of jsx) {
|
|
64
|
-
for (const attr of el.getAttributes()) {
|
|
65
|
-
if (!Node.isJsxAttribute(attr)) continue;
|
|
48
|
+
visit(source);
|
|
66
49
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const init = attr.getInitializer();
|
|
71
|
-
if (!init) continue;
|
|
72
|
-
|
|
73
|
-
const expr = Node.isJsxExpression(init)
|
|
74
|
-
? init.getExpression()
|
|
75
|
-
: init;
|
|
50
|
+
return events;
|
|
51
|
+
}
|
|
76
52
|
|
|
77
|
-
|
|
53
|
+
function getCallName(expr: ts.Expression): string {
|
|
54
|
+
if (ts.isIdentifier(expr)) return expr.text;
|
|
78
55
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
}
|
|
56
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
57
|
+
return expr.name.text;
|
|
83
58
|
}
|
|
84
59
|
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function isValid(v: string) {
|
|
89
|
-
return !!v && v.length > 1 && !v.includes(" ");
|
|
60
|
+
return "";
|
|
90
61
|
}
|
package/src/types.ts
CHANGED
package/dist/engine/extract.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractEventsFromArgs = extractEventsFromArgs;
|
|
4
|
-
const ts_morph_1 = require("ts-morph");
|
|
5
|
-
const resolver_1 = require("./resolver");
|
|
6
|
-
function extractEventsFromArgs(args, checker) {
|
|
7
|
-
return args.flatMap(arg => extract(arg, checker));
|
|
8
|
-
}
|
|
9
|
-
function extract(node, checker) {
|
|
10
|
-
if (ts_morph_1.Node.isStringLiteral(node)) {
|
|
11
|
-
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
12
|
-
}
|
|
13
|
-
if (ts_morph_1.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
14
|
-
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
15
|
-
}
|
|
16
|
-
if (ts_morph_1.Node.isTemplateExpression(node)) {
|
|
17
|
-
let result = node.getHead().getLiteralText();
|
|
18
|
-
for (const span of node.getTemplateSpans()) {
|
|
19
|
-
result += "*";
|
|
20
|
-
result += span.getLiteral().getLiteralText();
|
|
21
|
-
}
|
|
22
|
-
return [{ value: result, dynamic: true }];
|
|
23
|
-
}
|
|
24
|
-
if (ts_morph_1.Node.isObjectLiteralExpression(node)) {
|
|
25
|
-
return extractObject(node, checker);
|
|
26
|
-
}
|
|
27
|
-
const resolved = (0, resolver_1.resolveNodeValue)(node, checker);
|
|
28
|
-
if (resolved) {
|
|
29
|
-
return resolved.values.map(v => ({
|
|
30
|
-
value: v,
|
|
31
|
-
dynamic: resolved.dynamic,
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
function extractObject(obj, checker) {
|
|
37
|
-
const res = [];
|
|
38
|
-
for (const prop of obj.getProperties()) {
|
|
39
|
-
if (!ts_morph_1.Node.isPropertyAssignment(prop))
|
|
40
|
-
continue;
|
|
41
|
-
const name = prop.getName();
|
|
42
|
-
const init = prop.getInitializer();
|
|
43
|
-
if (!init)
|
|
44
|
-
continue;
|
|
45
|
-
if (["event", "name", "action", "type"].includes(name)) {
|
|
46
|
-
res.push(...extract(init, checker));
|
|
47
|
-
}
|
|
48
|
-
if (ts_morph_1.Node.isObjectLiteralExpression(init)) {
|
|
49
|
-
res.push(...extractObject(init, checker));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return res;
|
|
53
|
-
}
|
package/dist/engine/flow.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getForwardedParamIndex = getForwardedParamIndex;
|
|
4
|
-
const ts_morph_1 = require("ts-morph");
|
|
5
|
-
function getBodyNodes(fn) {
|
|
6
|
-
const body = fn.getBody();
|
|
7
|
-
if (!body)
|
|
8
|
-
return [];
|
|
9
|
-
if (ts_morph_1.Node.isBlock(body))
|
|
10
|
-
return body.getStatements();
|
|
11
|
-
return [body];
|
|
12
|
-
}
|
|
13
|
-
function getForwardedParamIndex(fn) {
|
|
14
|
-
if (!ts_morph_1.Node.isFunctionDeclaration(fn) &&
|
|
15
|
-
!ts_morph_1.Node.isArrowFunction(fn) &&
|
|
16
|
-
!ts_morph_1.Node.isFunctionExpression(fn))
|
|
17
|
-
return null;
|
|
18
|
-
const f = fn;
|
|
19
|
-
const params = f.getParameters();
|
|
20
|
-
for (const node of getBodyNodes(f)) {
|
|
21
|
-
const calls = node.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
|
|
22
|
-
for (const call of calls) {
|
|
23
|
-
const name = call.getExpression().getText();
|
|
24
|
-
if (!/(track|event|log|analytics|capture|send)/i.test(name))
|
|
25
|
-
continue;
|
|
26
|
-
const args = call.getArguments();
|
|
27
|
-
for (let i = 0; i < args.length; i++) {
|
|
28
|
-
const arg = args[i];
|
|
29
|
-
if (ts_morph_1.Node.isIdentifier(arg)) {
|
|
30
|
-
const idx = params.findIndex(p => p.getName() === arg.getText());
|
|
31
|
-
if (idx !== -1)
|
|
32
|
-
return idx;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
}
|
package/dist/engine/project.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
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.ProjectGraph = void 0;
|
|
7
|
-
const ts_morph_1 = require("ts-morph");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
function normalize(file) {
|
|
11
|
-
return path_1.default.resolve(file);
|
|
12
|
-
}
|
|
13
|
-
function findTsConfig(start) {
|
|
14
|
-
let dir = path_1.default.dirname(start);
|
|
15
|
-
while (true) {
|
|
16
|
-
const file = path_1.default.join(dir, "tsconfig.json");
|
|
17
|
-
if (fs_1.default.existsSync(file))
|
|
18
|
-
return file;
|
|
19
|
-
const parent = path_1.default.dirname(dir);
|
|
20
|
-
if (parent === dir)
|
|
21
|
-
break;
|
|
22
|
-
dir = parent;
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
function getScriptKind(file) {
|
|
27
|
-
if (file.endsWith(".tsx"))
|
|
28
|
-
return ts_morph_1.ts.ScriptKind.TSX;
|
|
29
|
-
if (file.endsWith(".jsx"))
|
|
30
|
-
return ts_morph_1.ts.ScriptKind.JSX;
|
|
31
|
-
if (file.endsWith(".js"))
|
|
32
|
-
return ts_morph_1.ts.ScriptKind.JS;
|
|
33
|
-
if (file.endsWith(".vue"))
|
|
34
|
-
return ts_morph_1.ts.ScriptKind.TSX;
|
|
35
|
-
return ts_morph_1.ts.ScriptKind.TS;
|
|
36
|
-
}
|
|
37
|
-
class ProjectGraph {
|
|
38
|
-
project;
|
|
39
|
-
constructor(entry) {
|
|
40
|
-
const tsconfig = findTsConfig(entry);
|
|
41
|
-
this.project = tsconfig
|
|
42
|
-
? new ts_morph_1.Project({
|
|
43
|
-
tsConfigFilePath: tsconfig,
|
|
44
|
-
skipAddingFilesFromTsConfig: false,
|
|
45
|
-
})
|
|
46
|
-
: new ts_morph_1.Project({
|
|
47
|
-
useInMemoryFileSystem: true,
|
|
48
|
-
compilerOptions: {
|
|
49
|
-
allowJs: true,
|
|
50
|
-
jsx: ts_morph_1.ts.JsxEmit.Preserve,
|
|
51
|
-
moduleResolution: ts_morph_1.ts.ModuleResolutionKind.NodeJs,
|
|
52
|
-
target: ts_morph_1.ts.ScriptTarget.ESNext,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
getSource(file, content) {
|
|
57
|
-
const filePath = normalize(file);
|
|
58
|
-
let source = this.project.getSourceFile(filePath);
|
|
59
|
-
if (!source) {
|
|
60
|
-
source = this.project.createSourceFile(filePath, content, {
|
|
61
|
-
overwrite: true,
|
|
62
|
-
scriptKind: getScriptKind(filePath),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
source.replaceWithText(content);
|
|
67
|
-
}
|
|
68
|
-
return source;
|
|
69
|
-
}
|
|
70
|
-
getChecker() {
|
|
71
|
-
return this.project.getTypeChecker().compilerObject;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
exports.ProjectGraph = ProjectGraph;
|
package/dist/engine/symbol.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveNodeValue = resolveNodeValue;
|
|
4
|
-
const ts_morph_1 = require("ts-morph");
|
|
5
|
-
function resolveNodeValue(node, checker) {
|
|
6
|
-
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
7
|
-
const symbol = checker.getSymbolAtLocation(node.compilerNode);
|
|
8
|
-
if (!symbol)
|
|
9
|
-
return null;
|
|
10
|
-
const decls = symbol.getDeclarations() ?? [];
|
|
11
|
-
for (const d of decls) {
|
|
12
|
-
if (ts_morph_1.ts.isVariableDeclaration(d) && d.initializer) {
|
|
13
|
-
if (ts_morph_1.Node.isStringLiteral(d.initializer)) {
|
|
14
|
-
return {
|
|
15
|
-
values: [d.initializer.text],
|
|
16
|
-
dynamic: false
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (ts_morph_1.ts.isParameter(d) && d.initializer) {
|
|
21
|
-
if (ts_morph_1.Node.isStringLiteral(d.initializer)) {
|
|
22
|
-
return {
|
|
23
|
-
values: [d.initializer.text],
|
|
24
|
-
dynamic: false
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
package/src/engine/extract.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Node, ObjectLiteralExpression, ts } from "ts-morph";
|
|
2
|
-
import { resolveNodeValue } from "./resolver";
|
|
3
|
-
|
|
4
|
-
export type Extracted = {
|
|
5
|
-
value: string;
|
|
6
|
-
dynamic: boolean;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function extractEventsFromArgs(
|
|
10
|
-
args: Node[],
|
|
11
|
-
checker: ts.TypeChecker
|
|
12
|
-
): Extracted[] {
|
|
13
|
-
return args.flatMap(arg => extract(arg, checker));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function extract(node: Node, checker: ts.TypeChecker): Extracted[] {
|
|
17
|
-
if (Node.isStringLiteral(node)) {
|
|
18
|
-
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
22
|
-
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (Node.isTemplateExpression(node)) {
|
|
26
|
-
let result = node.getHead().getLiteralText();
|
|
27
|
-
|
|
28
|
-
for (const span of node.getTemplateSpans()) {
|
|
29
|
-
result += "*";
|
|
30
|
-
result += span.getLiteral().getLiteralText();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return [{ value: result, dynamic: true }];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (Node.isObjectLiteralExpression(node)) {
|
|
37
|
-
return extractObject(node, checker);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const resolved = resolveNodeValue(node, checker);
|
|
41
|
-
if (resolved) {
|
|
42
|
-
return resolved.values.map(v => ({
|
|
43
|
-
value: v,
|
|
44
|
-
dynamic: resolved.dynamic,
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function extractObject(
|
|
52
|
-
obj: ObjectLiteralExpression,
|
|
53
|
-
checker: ts.TypeChecker
|
|
54
|
-
): Extracted[] {
|
|
55
|
-
const res: Extracted[] = [];
|
|
56
|
-
|
|
57
|
-
for (const prop of obj.getProperties()) {
|
|
58
|
-
if (!Node.isPropertyAssignment(prop)) continue;
|
|
59
|
-
|
|
60
|
-
const name = prop.getName();
|
|
61
|
-
const init = prop.getInitializer();
|
|
62
|
-
if (!init) continue;
|
|
63
|
-
|
|
64
|
-
if (["event", "name", "action", "type"].includes(name)) {
|
|
65
|
-
res.push(...extract(init, checker));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (Node.isObjectLiteralExpression(init)) {
|
|
69
|
-
res.push(...extractObject(init, checker));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return res;
|
|
74
|
-
}
|
package/src/engine/flow.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Node,
|
|
3
|
-
SyntaxKind,
|
|
4
|
-
FunctionDeclaration,
|
|
5
|
-
ArrowFunction,
|
|
6
|
-
FunctionExpression
|
|
7
|
-
} from "ts-morph";
|
|
8
|
-
|
|
9
|
-
type Fn =
|
|
10
|
-
| FunctionDeclaration
|
|
11
|
-
| ArrowFunction
|
|
12
|
-
| FunctionExpression;
|
|
13
|
-
|
|
14
|
-
function getBodyNodes(fn: Fn) {
|
|
15
|
-
const body = fn.getBody();
|
|
16
|
-
if (!body) return [];
|
|
17
|
-
|
|
18
|
-
if (Node.isBlock(body)) return body.getStatements();
|
|
19
|
-
return [body];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function getForwardedParamIndex(fn: Node): number | null {
|
|
23
|
-
if (
|
|
24
|
-
!Node.isFunctionDeclaration(fn) &&
|
|
25
|
-
!Node.isArrowFunction(fn) &&
|
|
26
|
-
!Node.isFunctionExpression(fn)
|
|
27
|
-
) return null;
|
|
28
|
-
|
|
29
|
-
const f = fn as Fn;
|
|
30
|
-
const params = f.getParameters();
|
|
31
|
-
|
|
32
|
-
for (const node of getBodyNodes(f)) {
|
|
33
|
-
const calls = node.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
34
|
-
|
|
35
|
-
for (const call of calls) {
|
|
36
|
-
const name = call.getExpression().getText();
|
|
37
|
-
|
|
38
|
-
if (!/(track|event|log|analytics|capture|send)/i.test(name)) continue;
|
|
39
|
-
|
|
40
|
-
const args = call.getArguments();
|
|
41
|
-
|
|
42
|
-
for (let i = 0; i < args.length; i++) {
|
|
43
|
-
const arg = args[i];
|
|
44
|
-
|
|
45
|
-
if (Node.isIdentifier(arg)) {
|
|
46
|
-
const idx = params.findIndex(p => p.getName() === arg.getText());
|
|
47
|
-
if (idx !== -1) return idx;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return null;
|
|
54
|
-
}
|
package/src/engine/project.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { Project, SourceFile, ts } from "ts-morph";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
|
|
5
|
-
function normalize(file: string) {
|
|
6
|
-
return path.resolve(file);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function findTsConfig(start: string): string | null {
|
|
10
|
-
let dir = path.dirname(start);
|
|
11
|
-
|
|
12
|
-
while (true) {
|
|
13
|
-
const file = path.join(dir, "tsconfig.json");
|
|
14
|
-
if (fs.existsSync(file)) return file;
|
|
15
|
-
|
|
16
|
-
const parent = path.dirname(dir);
|
|
17
|
-
if (parent === dir) break;
|
|
18
|
-
|
|
19
|
-
dir = parent;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getScriptKind(file: string): ts.ScriptKind {
|
|
26
|
-
if (file.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
27
|
-
if (file.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
28
|
-
if (file.endsWith(".js")) return ts.ScriptKind.JS;
|
|
29
|
-
if (file.endsWith(".vue")) return ts.ScriptKind.TSX;
|
|
30
|
-
return ts.ScriptKind.TS;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class ProjectGraph {
|
|
34
|
-
private project: Project;
|
|
35
|
-
|
|
36
|
-
constructor(entry: string) {
|
|
37
|
-
const tsconfig = findTsConfig(entry);
|
|
38
|
-
|
|
39
|
-
this.project = tsconfig
|
|
40
|
-
? new Project({
|
|
41
|
-
tsConfigFilePath: tsconfig,
|
|
42
|
-
skipAddingFilesFromTsConfig: false,
|
|
43
|
-
})
|
|
44
|
-
: new Project({
|
|
45
|
-
useInMemoryFileSystem: true,
|
|
46
|
-
compilerOptions: {
|
|
47
|
-
allowJs: true,
|
|
48
|
-
jsx: ts.JsxEmit.Preserve,
|
|
49
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
50
|
-
target: ts.ScriptTarget.ESNext,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
getSource(file: string, content: string): SourceFile {
|
|
56
|
-
const filePath = normalize(file);
|
|
57
|
-
|
|
58
|
-
let source = this.project.getSourceFile(filePath);
|
|
59
|
-
|
|
60
|
-
if (!source) {
|
|
61
|
-
source = this.project.createSourceFile(filePath, content, {
|
|
62
|
-
overwrite: true,
|
|
63
|
-
scriptKind: getScriptKind(filePath),
|
|
64
|
-
});
|
|
65
|
-
} else {
|
|
66
|
-
source.replaceWithText(content);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return source;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
getChecker() {
|
|
73
|
-
return this.project.getTypeChecker().compilerObject;
|
|
74
|
-
}
|
|
75
|
-
}
|