@eventra_dev/eventra-cli 0.0.19 → 0.1.1
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/commands/check.js +17 -117
- package/dist/commands/sync.js +20 -115
- package/dist/commands/watch.js +107 -69
- package/dist/engine/callResolver.js +31 -0
- package/dist/engine/engine.js +33 -0
- package/dist/engine/extract.js +53 -0
- package/dist/engine/flow.js +38 -0
- package/dist/engine/graph.js +22 -0
- package/dist/engine/project.js +74 -0
- package/dist/engine/resolver.js +38 -0
- package/dist/engine/scanner.js +78 -0
- package/dist/engine/symbol.js +31 -0
- package/dist/utils/parsers/vue.js +16 -12
- package/dist/utils/processFile.js +20 -0
- package/package.json +1 -1
- package/src/commands/check.ts +19 -151
- package/src/commands/sync.ts +23 -194
- package/src/commands/watch.ts +119 -66
- package/src/engine/callResolver.ts +37 -0
- package/src/engine/engine.ts +39 -0
- package/src/engine/extract.ts +74 -0
- package/src/engine/flow.ts +54 -0
- package/src/engine/graph.ts +23 -0
- package/src/engine/project.ts +75 -0
- package/src/engine/resolver.ts +53 -0
- package/src/engine/scanner.ts +90 -0
- package/src/engine/symbol.ts +42 -0
- package/src/types.ts +3 -9
- package/src/utils/parsers/vue.ts +19 -20
- package/src/utils/processFile.ts +18 -0
- package/tests/fixtures/backend/express/app.ts +167 -12
- package/tests/fixtures/wrappers/component/test.tsx +1 -1
- package/tests/run.ts +4 -17
- package/dist/utils/detect-aliases.js +0 -39
- package/dist/utils/detect-wrappers.js +0 -84
- package/dist/utils/extract.js +0 -56
- package/dist/utils/scanners/component-wrappers.js +0 -73
- package/dist/utils/scanners/function-wrappers.js +0 -83
- package/dist/utils/scanners/track.js +0 -64
- package/src/utils/detect-aliases.ts +0 -46
- package/src/utils/detect-wrappers.ts +0 -119
- package/src/utils/extract.ts +0 -72
- package/src/utils/scanners/component-wrappers.ts +0 -109
- package/src/utils/scanners/function-wrappers.ts +0 -137
- package/src/utils/scanners/track.ts +0 -99
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SourceFile } from "ts-morph";
|
|
2
|
+
|
|
3
|
+
export class DependencyGraph {
|
|
4
|
+
private reverse = new Map<string, Set<string>>();
|
|
5
|
+
|
|
6
|
+
update(file: string, source: SourceFile) {
|
|
7
|
+
const imports = source
|
|
8
|
+
.getImportDeclarations()
|
|
9
|
+
.map(i => i.getModuleSpecifierSourceFile()?.getFilePath())
|
|
10
|
+
.filter(Boolean) as string[];
|
|
11
|
+
|
|
12
|
+
for (const dep of imports) {
|
|
13
|
+
if (!this.reverse.has(dep)) {
|
|
14
|
+
this.reverse.set(dep, new Set());
|
|
15
|
+
}
|
|
16
|
+
this.reverse.get(dep)!.add(file);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getDependents(file: string) {
|
|
21
|
+
return [...(this.reverse.get(file) ?? [])];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Node, ts } from "ts-morph";
|
|
2
|
+
|
|
3
|
+
export type ResolveResult = {
|
|
4
|
+
values: string[];
|
|
5
|
+
dynamic: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function resolveNodeValue(
|
|
9
|
+
node: Node,
|
|
10
|
+
checker: ts.TypeChecker
|
|
11
|
+
): ResolveResult | null {
|
|
12
|
+
if (!Node.isIdentifier(node)) return null;
|
|
13
|
+
|
|
14
|
+
const symbol = checker.getSymbolAtLocation(node.compilerNode);
|
|
15
|
+
if (!symbol) return null;
|
|
16
|
+
|
|
17
|
+
const decls = symbol.getDeclarations() ?? [];
|
|
18
|
+
|
|
19
|
+
for (const d of decls) {
|
|
20
|
+
if (ts.isVariableDeclaration(d) && d.initializer) {
|
|
21
|
+
const init = d.initializer;
|
|
22
|
+
|
|
23
|
+
if (ts.isStringLiteral(init)) {
|
|
24
|
+
return { values: [init.text], dynamic: false };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
28
|
+
return { values: [init.text], dynamic: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (ts.isTemplateExpression(init)) {
|
|
32
|
+
let result = init.head.text;
|
|
33
|
+
|
|
34
|
+
for (const span of init.templateSpans) {
|
|
35
|
+
result += "*";
|
|
36
|
+
result += span.literal.text;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { values: [result], dynamic: true };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (ts.isParameter(d) && d.initializer) {
|
|
44
|
+
const init = d.initializer;
|
|
45
|
+
|
|
46
|
+
if (ts.isStringLiteral(init)) {
|
|
47
|
+
return { values: [init.text], dynamic: false };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { SourceFile, SyntaxKind, Node, ts } from "ts-morph";
|
|
2
|
+
import { extractEventsFromArgs } from "./extract";
|
|
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
|
+
|
|
25
|
+
export function scanSource(
|
|
26
|
+
source: SourceFile,
|
|
27
|
+
checker: ts.TypeChecker
|
|
28
|
+
) {
|
|
29
|
+
const events = new Set<string>();
|
|
30
|
+
|
|
31
|
+
// CALLS
|
|
32
|
+
for (const call of source.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
33
|
+
const expr = call.getExpression();
|
|
34
|
+
const name = getCallName(expr);
|
|
35
|
+
|
|
36
|
+
if (!isTrackLike(name)) continue;
|
|
37
|
+
|
|
38
|
+
const args = call.getArguments();
|
|
39
|
+
if (!args.length) continue;
|
|
40
|
+
|
|
41
|
+
let target = pickArg(args);
|
|
42
|
+
|
|
43
|
+
const fn = resolveFunctionFromCall(expr);
|
|
44
|
+
|
|
45
|
+
if (fn) {
|
|
46
|
+
const index = getForwardedParamIndex(fn);
|
|
47
|
+
if (index !== null && args[index]) {
|
|
48
|
+
target = args[index];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
extractEventsFromArgs([target], checker).forEach(({ value }) => {
|
|
53
|
+
if (isValid(value)) events.add(value);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// JSX
|
|
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;
|
|
66
|
+
|
|
67
|
+
const name = attr.getNameNode().getText();
|
|
68
|
+
if (!/(event|name|action)/i.test(name)) continue;
|
|
69
|
+
|
|
70
|
+
const init = attr.getInitializer();
|
|
71
|
+
if (!init) continue;
|
|
72
|
+
|
|
73
|
+
const expr = Node.isJsxExpression(init)
|
|
74
|
+
? init.getExpression()
|
|
75
|
+
: init;
|
|
76
|
+
|
|
77
|
+
if (!expr) continue;
|
|
78
|
+
|
|
79
|
+
extractEventsFromArgs([expr], checker).forEach(({ value }) => {
|
|
80
|
+
if (isValid(value)) events.add(value);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return events;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isValid(v: string) {
|
|
89
|
+
return !!v && v.length > 1 && !v.includes(" ");
|
|
90
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Node, ts } from "ts-morph";
|
|
2
|
+
|
|
3
|
+
export type ResolveResult = {
|
|
4
|
+
values: string[];
|
|
5
|
+
dynamic: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function resolveNodeValue(
|
|
9
|
+
node: Node,
|
|
10
|
+
checker: ts.TypeChecker
|
|
11
|
+
): ResolveResult | null {
|
|
12
|
+
|
|
13
|
+
if (Node.isIdentifier(node)) {
|
|
14
|
+
const symbol = checker.getSymbolAtLocation(node.compilerNode);
|
|
15
|
+
if (!symbol) return null;
|
|
16
|
+
|
|
17
|
+
const decls = symbol.getDeclarations() ?? [];
|
|
18
|
+
|
|
19
|
+
for (const d of decls) {
|
|
20
|
+
|
|
21
|
+
if (ts.isVariableDeclaration(d) && d.initializer) {
|
|
22
|
+
if (Node.isStringLiteral(d.initializer as any)) {
|
|
23
|
+
return {
|
|
24
|
+
values: [(d.initializer as any).text],
|
|
25
|
+
dynamic: false
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (ts.isParameter(d) && d.initializer) {
|
|
31
|
+
if (Node.isStringLiteral(d.initializer as any)) {
|
|
32
|
+
return {
|
|
33
|
+
values: [(d.initializer as any).text],
|
|
34
|
+
dynamic: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null;
|
|
42
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type ScanMode = "strict" | "hybrid" | "discovery";
|
|
2
|
+
|
|
1
3
|
export type ComponentWrapper = {
|
|
2
4
|
name: string;
|
|
3
5
|
prop: string;
|
|
@@ -6,14 +8,13 @@ export type ComponentWrapper = {
|
|
|
6
8
|
export type FunctionWrapper = {
|
|
7
9
|
name: string;
|
|
8
10
|
event?: string;
|
|
9
|
-
file?: string;
|
|
10
|
-
line?: number;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export type EventraConfig = {
|
|
14
14
|
apiKey?: string;
|
|
15
15
|
endpoint?: string;
|
|
16
16
|
events: string[];
|
|
17
|
+
mode?: ScanMode;
|
|
17
18
|
wrappers: ComponentWrapper[];
|
|
18
19
|
functionWrappers: FunctionWrapper[];
|
|
19
20
|
aliases?: Record<string, string>;
|
|
@@ -22,10 +23,3 @@ export type EventraConfig = {
|
|
|
22
23
|
exclude: string[];
|
|
23
24
|
};
|
|
24
25
|
};
|
|
25
|
-
|
|
26
|
-
export type ExtractedEvent = {
|
|
27
|
-
value: string;
|
|
28
|
-
dynamic: boolean;
|
|
29
|
-
file?: string;
|
|
30
|
-
line?: number;
|
|
31
|
-
};
|
package/src/utils/parsers/vue.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
export function parseVue(
|
|
2
|
-
content
|
|
3
|
-
)
|
|
4
|
-
const template =
|
|
5
|
-
content.match(
|
|
6
|
-
/<template[\s\S]*?>([\s\S]*?)<\/template>/
|
|
7
|
-
);
|
|
1
|
+
export function parseVue(content: string) {
|
|
2
|
+
const scriptMatch = content.match(/<script[\s\S]*?>([\s\S]*?)<\/script>/);
|
|
3
|
+
const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
|
|
8
4
|
|
|
9
|
-
const script =
|
|
10
|
-
|
|
11
|
-
/<script[\s\S]*?>([\s\S]*?)<\/script>/
|
|
12
|
-
);
|
|
5
|
+
const script = scriptMatch?.[1] ?? "";
|
|
6
|
+
let template = templateMatch?.[1] ?? "";
|
|
13
7
|
|
|
14
|
-
|
|
15
|
-
${script?.[1] ?? ""}
|
|
8
|
+
template = template.replace(/<!--[\s\S]*?-->/g, "");
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
template = template
|
|
11
|
+
.replace(/v-if="[^"]+"/g, "")
|
|
12
|
+
.replace(/v-for="[^"]+"/g, "")
|
|
13
|
+
.replace(/@click=/g, "onClick=");
|
|
14
|
+
|
|
15
|
+
const jsx = `
|
|
16
|
+
const __VUE_JSX__ = (
|
|
17
|
+
<>
|
|
18
|
+
${template}
|
|
19
|
+
</>
|
|
20
|
+
);
|
|
24
21
|
`;
|
|
22
|
+
|
|
23
|
+
return script + "\n" + jsx;
|
|
25
24
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { detectParser } from "./parsers/router";
|
|
2
|
+
import { parseVue } from "./parsers/vue";
|
|
3
|
+
import { parseSvelte } from "./parsers/svelte";
|
|
4
|
+
import { parseAstro } from "./parsers/astro";
|
|
5
|
+
|
|
6
|
+
export function processFile(file: string, content: string) {
|
|
7
|
+
const parser = detectParser(file);
|
|
8
|
+
|
|
9
|
+
if (parser === "vue") content = parseVue(content);
|
|
10
|
+
if (parser === "svelte") content = parseSvelte(content);
|
|
11
|
+
if (parser === "astro") content = parseAstro(content);
|
|
12
|
+
|
|
13
|
+
const virtualFile = file.endsWith(".vue")
|
|
14
|
+
? file + ".tsx"
|
|
15
|
+
: file;
|
|
16
|
+
|
|
17
|
+
return { content, virtualFile };
|
|
18
|
+
}
|
|
@@ -13,66 +13,221 @@ const express = (...args: any[]) => ({
|
|
|
13
13
|
|
|
14
14
|
const app = express();
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// =========================
|
|
17
|
+
// BASIC
|
|
18
|
+
// =========================
|
|
19
|
+
|
|
17
20
|
track("express_event");
|
|
18
21
|
|
|
19
|
-
//
|
|
22
|
+
// =========================
|
|
23
|
+
// VARIABLES
|
|
24
|
+
// =========================
|
|
25
|
+
|
|
26
|
+
const eventName = "variable_event";
|
|
27
|
+
track(eventName);
|
|
28
|
+
|
|
29
|
+
let anotherEvent;
|
|
30
|
+
anotherEvent = "assigned_event";
|
|
31
|
+
track(anotherEvent);
|
|
32
|
+
|
|
33
|
+
// =========================
|
|
34
|
+
// TEMPLATE
|
|
35
|
+
// =========================
|
|
36
|
+
|
|
37
|
+
track(`template_event`);
|
|
38
|
+
|
|
39
|
+
const id = "123";
|
|
40
|
+
track(`template_${id}`); // dynamic
|
|
41
|
+
|
|
42
|
+
// =========================
|
|
43
|
+
// MIDDLEWARE
|
|
44
|
+
// =========================
|
|
45
|
+
|
|
20
46
|
app.use(() => {
|
|
21
47
|
track("middleware_event");
|
|
22
48
|
});
|
|
23
49
|
|
|
24
|
-
//
|
|
50
|
+
// =========================
|
|
51
|
+
// ROUTES
|
|
52
|
+
// =========================
|
|
53
|
+
|
|
25
54
|
app.get("/", () => {
|
|
26
55
|
track("get_event");
|
|
27
56
|
});
|
|
28
57
|
|
|
29
|
-
// ===== post =====
|
|
30
58
|
app.post("/users", () => {
|
|
31
59
|
track("post_event");
|
|
32
60
|
});
|
|
33
61
|
|
|
34
|
-
// ===== put =====
|
|
35
62
|
app.put("/users", () => {
|
|
36
63
|
track("put_event");
|
|
37
64
|
});
|
|
38
65
|
|
|
39
|
-
// ===== delete =====
|
|
40
66
|
app.delete("/users", () => {
|
|
41
67
|
track("delete_event");
|
|
42
68
|
});
|
|
43
69
|
|
|
44
|
-
//
|
|
70
|
+
// =========================
|
|
71
|
+
// NESTED
|
|
72
|
+
// =========================
|
|
73
|
+
|
|
45
74
|
app.get("/nested", () => {
|
|
46
75
|
if (true) {
|
|
47
76
|
track("nested_event");
|
|
48
77
|
}
|
|
49
78
|
});
|
|
50
79
|
|
|
51
|
-
//
|
|
80
|
+
// =========================
|
|
81
|
+
// ASYNC
|
|
82
|
+
// =========================
|
|
83
|
+
|
|
52
84
|
app.get("/async", async () => {
|
|
53
85
|
track("async_event");
|
|
54
86
|
});
|
|
55
87
|
|
|
56
|
-
//
|
|
88
|
+
// =========================
|
|
89
|
+
// ARROW HANDLER
|
|
90
|
+
// =========================
|
|
91
|
+
|
|
57
92
|
const handler = () => {
|
|
58
93
|
track("arrow_event");
|
|
59
94
|
};
|
|
60
95
|
|
|
61
96
|
app.get("/arrow", handler);
|
|
62
97
|
|
|
63
|
-
//
|
|
98
|
+
// =========================
|
|
99
|
+
// FUNCTION DECLARATION
|
|
100
|
+
// =========================
|
|
101
|
+
|
|
64
102
|
function service() {
|
|
65
103
|
track("function_event");
|
|
66
104
|
}
|
|
67
105
|
|
|
68
|
-
//
|
|
106
|
+
// =========================
|
|
107
|
+
// CLASS
|
|
108
|
+
// =========================
|
|
109
|
+
|
|
69
110
|
class Service {
|
|
70
111
|
run() {
|
|
71
112
|
track("class_event");
|
|
72
113
|
}
|
|
73
114
|
}
|
|
74
115
|
|
|
75
|
-
//
|
|
116
|
+
// =========================
|
|
117
|
+
// LISTEN
|
|
118
|
+
// =========================
|
|
119
|
+
|
|
76
120
|
app.listen(3000, () => {
|
|
77
121
|
track("listen_event");
|
|
78
122
|
});
|
|
123
|
+
|
|
124
|
+
// =========================
|
|
125
|
+
// OBJECT WRAPPER
|
|
126
|
+
// =========================
|
|
127
|
+
|
|
128
|
+
const analytics = {
|
|
129
|
+
track
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
analytics.track("object_event");
|
|
133
|
+
|
|
134
|
+
// nested object
|
|
135
|
+
const analyticsDeep = {
|
|
136
|
+
events: {
|
|
137
|
+
track
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
analyticsDeep.events.track("nested_object_event");
|
|
142
|
+
|
|
143
|
+
// =========================
|
|
144
|
+
// CONDITIONALS
|
|
145
|
+
// =========================
|
|
146
|
+
|
|
147
|
+
if (true) {
|
|
148
|
+
track("if_event");
|
|
149
|
+
} else {
|
|
150
|
+
track("else_event");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ternary
|
|
154
|
+
true
|
|
155
|
+
? track("ternary_true")
|
|
156
|
+
: track("ternary_false");
|
|
157
|
+
|
|
158
|
+
// =========================
|
|
159
|
+
// ARRAYS
|
|
160
|
+
// =========================
|
|
161
|
+
|
|
162
|
+
[
|
|
163
|
+
() => track("array_1"),
|
|
164
|
+
() => track("array_2")
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
// =========================
|
|
168
|
+
// LOOP
|
|
169
|
+
// =========================
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < 1; i++) {
|
|
172
|
+
track("loop_event");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// =========================
|
|
176
|
+
// TRY / CATCH
|
|
177
|
+
// =========================
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
track("try_event");
|
|
181
|
+
} catch {
|
|
182
|
+
track("catch_event");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// =========================
|
|
186
|
+
// RETURN
|
|
187
|
+
// =========================
|
|
188
|
+
|
|
189
|
+
function returnTest() {
|
|
190
|
+
return track("return_event");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// =========================
|
|
194
|
+
// PARAM DEFAULT
|
|
195
|
+
// =========================
|
|
196
|
+
|
|
197
|
+
function withDefault(e = "default_event") {
|
|
198
|
+
track(e);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// =========================
|
|
202
|
+
// INLINE OBJECT
|
|
203
|
+
// =========================
|
|
204
|
+
|
|
205
|
+
track({
|
|
206
|
+
name: "object_payload_event"
|
|
207
|
+
} as any);
|
|
208
|
+
|
|
209
|
+
// =========================
|
|
210
|
+
// OPTIONAL CHAINING
|
|
211
|
+
// =========================
|
|
212
|
+
|
|
213
|
+
analytics?.track?.("optional_chain_event");
|
|
214
|
+
|
|
215
|
+
// =========================
|
|
216
|
+
// EDGE CASES
|
|
217
|
+
// =========================
|
|
218
|
+
|
|
219
|
+
// not first argument (should be ignored usually)
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
track("real_event", "extra");
|
|
222
|
+
|
|
223
|
+
// no args
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
track();
|
|
226
|
+
|
|
227
|
+
// dynamic identifier (should be dynamic)
|
|
228
|
+
const dynamicEvent = Math.random() > 0.5 ? "a" : "b";
|
|
229
|
+
track(dynamicEvent);
|
|
230
|
+
|
|
231
|
+
// =========================
|
|
232
|
+
// END
|
|
233
|
+
// =========================
|
package/tests/run.ts
CHANGED
|
@@ -18,7 +18,7 @@ const fixtures = [
|
|
|
18
18
|
"wrappers/function"
|
|
19
19
|
];
|
|
20
20
|
|
|
21
|
-
function runCLI(args: string[], cwd: string) {
|
|
21
|
+
function runCLI(args: string[], cwd: string): string {
|
|
22
22
|
const result = spawnSync(
|
|
23
23
|
process.execPath,
|
|
24
24
|
[CLI, ...args],
|
|
@@ -35,23 +35,12 @@ function runCLI(args: string[], cwd: string) {
|
|
|
35
35
|
function ensureTestConfig(dir: string) {
|
|
36
36
|
const configPath = path.join(dir, "eventra.json");
|
|
37
37
|
|
|
38
|
-
if (fs.existsSync(configPath)) return;
|
|
39
|
-
|
|
40
38
|
const config = {
|
|
41
39
|
apiKey: "",
|
|
42
40
|
events: [],
|
|
43
|
-
wrappers: [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
prop: "event"
|
|
47
|
-
}
|
|
48
|
-
],
|
|
49
|
-
functionWrappers: [
|
|
50
|
-
{
|
|
51
|
-
name: "trackFeature",
|
|
52
|
-
path: "0"
|
|
53
|
-
}
|
|
54
|
-
],
|
|
41
|
+
wrappers: [],
|
|
42
|
+
functionWrappers: [],
|
|
43
|
+
aliases: {},
|
|
55
44
|
sync: {
|
|
56
45
|
include: [
|
|
57
46
|
"**/*.{ts,tsx,js,jsx,vue,svelte,astro}"
|
|
@@ -90,8 +79,6 @@ function runFixture(name: string) {
|
|
|
90
79
|
|
|
91
80
|
ensureTestConfig(dir);
|
|
92
81
|
|
|
93
|
-
runCLI(["init"], dir);
|
|
94
|
-
|
|
95
82
|
const output = runCLI(["sync"], dir);
|
|
96
83
|
|
|
97
84
|
const match =
|