@eventra_dev/eventra-cli 0.0.18 → 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.
Files changed (44) hide show
  1. package/README.md +74 -166
  2. package/dist/commands/check.js +25 -88
  3. package/dist/commands/sync.js +22 -77
  4. package/dist/commands/watch.js +135 -0
  5. package/dist/engine/callResolver.js +31 -0
  6. package/dist/engine/engine.js +33 -0
  7. package/dist/engine/extract.js +53 -0
  8. package/dist/engine/flow.js +38 -0
  9. package/dist/engine/graph.js +22 -0
  10. package/dist/engine/project.js +74 -0
  11. package/dist/engine/resolver.js +38 -0
  12. package/dist/engine/scanner.js +78 -0
  13. package/dist/engine/symbol.js +31 -0
  14. package/dist/index.js +10 -6
  15. package/dist/utils/parsers/vue.js +16 -12
  16. package/dist/utils/processFile.js +20 -0
  17. package/package.json +2 -1
  18. package/src/commands/check.ts +29 -136
  19. package/src/commands/sync.ts +24 -135
  20. package/src/commands/watch.ts +166 -0
  21. package/src/engine/callResolver.ts +37 -0
  22. package/src/engine/engine.ts +39 -0
  23. package/src/engine/extract.ts +74 -0
  24. package/src/engine/flow.ts +54 -0
  25. package/src/engine/graph.ts +23 -0
  26. package/src/engine/project.ts +75 -0
  27. package/src/engine/resolver.ts +53 -0
  28. package/src/engine/scanner.ts +90 -0
  29. package/src/engine/symbol.ts +42 -0
  30. package/src/index.ts +11 -6
  31. package/src/types.ts +3 -5
  32. package/src/utils/parsers/vue.ts +19 -20
  33. package/src/utils/processFile.ts +18 -0
  34. package/tests/fixtures/backend/express/app.ts +167 -12
  35. package/tests/fixtures/wrappers/component/test.tsx +1 -1
  36. package/tests/run.ts +4 -17
  37. package/dist/utils/extract.js +0 -136
  38. package/dist/utils/scanners/component-wrappers.js +0 -70
  39. package/dist/utils/scanners/function-wrappers.js +0 -85
  40. package/dist/utils/scanners/track.js +0 -52
  41. package/src/utils/extract.ts +0 -185
  42. package/src/utils/scanners/component-wrappers.ts +0 -107
  43. package/src/utils/scanners/function-wrappers.ts +0 -180
  44. package/src/utils/scanners/track.ts +0 -84
@@ -43,7 +43,7 @@ export default function WrapperTest() {
43
43
  ]}
44
44
 
45
45
  {/* wrapper components */}
46
- <TrackedButton event="tracked_button" />
46
+ <TrackedButton event={eventName} />
47
47
  <MyButton event="my_button" />
48
48
 
49
49
  {/* multiline */}
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
- name: "Button",
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 =
@@ -1,136 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractExpression = extractExpression;
4
- const ts_morph_1 = require("ts-morph");
5
- function extractExpression(expr) {
6
- // "event"
7
- if (ts_morph_1.Node.isStringLiteral(expr)) {
8
- return {
9
- values: [expr.getLiteralText()],
10
- dynamic: false
11
- };
12
- }
13
- // `event`
14
- if (expr.getKind() ===
15
- ts_morph_1.SyntaxKind.NoSubstitutionTemplateLiteral) {
16
- return {
17
- values: [
18
- expr.getText().replace(/`/g, "")
19
- ],
20
- dynamic: false
21
- };
22
- }
23
- // identifier (TEST)
24
- if (ts_morph_1.Node.isIdentifier(expr)) {
25
- const name = expr.getText();
26
- const defs = "getDefinitions" in expr
27
- ? expr.getDefinitions()
28
- : [];
29
- if (defs && defs.length > 0) {
30
- const decl = defs[0]?.getDeclarationNode();
31
- if (decl && ts_morph_1.Node.isVariableDeclaration(decl)) {
32
- const initializer = decl.getInitializer();
33
- // const TEST = "event"
34
- if (initializer &&
35
- ts_morph_1.Node.isStringLiteral(initializer)) {
36
- return {
37
- values: [initializer.getLiteralText()],
38
- dynamic: false
39
- };
40
- }
41
- // const TEST = `event`
42
- if (initializer &&
43
- initializer.getKind() ===
44
- ts_morph_1.SyntaxKind.NoSubstitutionTemplateLiteral) {
45
- return {
46
- values: [
47
- initializer.getText().replace(/`/g, "")
48
- ],
49
- dynamic: false
50
- };
51
- }
52
- }
53
- }
54
- return {
55
- values: [name],
56
- dynamic: true
57
- };
58
- }
59
- // property access (EVENTS.CLICK)
60
- if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
61
- const fullName = expr.getText(); // EVENTS.CLICK
62
- const propName = expr.getName(); // CLICK
63
- const defs = "getDefinitions" in expr
64
- ? expr.getDefinitions()
65
- : [];
66
- if (defs && defs.length > 0) {
67
- const decl = defs[0]?.getDeclarationNode();
68
- // const EVENTS = { CLICK: "event" }
69
- if (decl && ts_morph_1.Node.isVariableDeclaration(decl)) {
70
- const init = decl.getInitializer();
71
- if (init && ts_morph_1.Node.isObjectLiteralExpression(init)) {
72
- const prop = init.getProperty(propName);
73
- if (prop &&
74
- ts_morph_1.Node.isPropertyAssignment(prop)) {
75
- const value = prop.getInitializer();
76
- if (value &&
77
- ts_morph_1.Node.isStringLiteral(value)) {
78
- return {
79
- values: [value.getLiteralText()],
80
- dynamic: false
81
- };
82
- }
83
- }
84
- }
85
- }
86
- // fallback
87
- if (decl && ts_morph_1.Node.isPropertyAssignment(decl)) {
88
- const initializer = decl.getInitializer();
89
- if (initializer &&
90
- ts_morph_1.Node.isStringLiteral(initializer)) {
91
- return {
92
- values: [initializer.getLiteralText()],
93
- dynamic: false
94
- };
95
- }
96
- }
97
- }
98
- return {
99
- values: [fullName],
100
- dynamic: true
101
- };
102
- }
103
- // ternary (a ? b : c)
104
- if (ts_morph_1.Node.isConditionalExpression(expr)) {
105
- const values = [];
106
- const whenTrue = extractExpression(expr.getWhenTrue());
107
- const whenFalse = extractExpression(expr.getWhenFalse());
108
- if (whenTrue)
109
- values.push(...whenTrue.values);
110
- if (whenFalse)
111
- values.push(...whenFalse.values);
112
- return {
113
- values,
114
- dynamic: true
115
- };
116
- }
117
- // template `${}`
118
- if (ts_morph_1.Node.isTemplateExpression(expr)) {
119
- return {
120
- values: [
121
- expr.getText().replace(/`/g, "")
122
- ],
123
- dynamic: true
124
- };
125
- }
126
- // call (fn())
127
- if (ts_morph_1.Node.isCallExpression(expr)) {
128
- return {
129
- values: [
130
- expr.getExpression().getText()
131
- ],
132
- dynamic: true
133
- };
134
- }
135
- return null;
136
- }
@@ -1,70 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scanComponentWrappers = scanComponentWrappers;
4
- const ts_morph_1 = require("ts-morph");
5
- const extract_1 = require("../extract");
6
- function scanComponentWrappers(source, wrappers) {
7
- const events = new Set();
8
- const elements = [
9
- ...source.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxOpeningElement),
10
- ...source.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement)
11
- ];
12
- for (const el of elements) {
13
- const name = el
14
- .getTagNameNode()
15
- .getText()
16
- .toLowerCase();
17
- for (const wrapper of wrappers) {
18
- if (name !== wrapper.name.toLowerCase())
19
- continue;
20
- const attrs = el.getAttributes();
21
- for (const attr of attrs) {
22
- // ❗ NO spread props
23
- if (ts_morph_1.Node.isJsxSpreadAttribute(attr)) {
24
- continue;
25
- }
26
- const attrNode = attr.asKind(ts_morph_1.SyntaxKind.JsxAttribute);
27
- if (!attrNode)
28
- continue;
29
- const key = attrNode
30
- .getNameNode()
31
- .getText()
32
- .toLowerCase();
33
- if (key !== wrapper.prop.toLowerCase())
34
- continue;
35
- const init = attrNode.getInitializer();
36
- // <Button event />
37
- if (!init) {
38
- events.add({
39
- value: key,
40
- dynamic: true
41
- });
42
- continue;
43
- }
44
- // string: <Button event="click" />
45
- if (ts_morph_1.Node.isStringLiteral(init)) {
46
- events.add({
47
- value: init.getLiteralText(),
48
- dynamic: false
49
- });
50
- continue;
51
- }
52
- // jsx expression: <Button event={"click"} />
53
- if (ts_morph_1.Node.isJsxExpression(init)) {
54
- const expr = init.getExpression();
55
- if (!expr)
56
- continue;
57
- const result = (0, extract_1.extractExpression)(expr);
58
- if (!result)
59
- continue;
60
- result.values.forEach((value) => events.add({
61
- value,
62
- dynamic: result.dynamic
63
- }));
64
- continue;
65
- }
66
- }
67
- }
68
- }
69
- return events;
70
- }
@@ -1,85 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scanFunctionWrappers = scanFunctionWrappers;
4
- const ts_morph_1 = require("ts-morph");
5
- const extract_1 = require("../extract");
6
- function scanFunctionWrappers(source, wrappers) {
7
- const events = new Set();
8
- const calls = source.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
9
- for (const call of calls) {
10
- const name = getFunctionName(call);
11
- if (!name)
12
- continue;
13
- for (const wrapper of wrappers) {
14
- if (wrapper.name !== name)
15
- continue;
16
- const results = extractEventFromArgs(call, wrapper.event);
17
- if (!results)
18
- continue;
19
- results.forEach((r) => events.add(r));
20
- }
21
- }
22
- return events;
23
- }
24
- function getFunctionName(call) {
25
- const expression = call.getExpression();
26
- if (ts_morph_1.Node.isIdentifier(expression)) {
27
- return expression.getText();
28
- }
29
- if (ts_morph_1.Node.isPropertyAccessExpression(expression)) {
30
- return getDeepName(expression);
31
- }
32
- return null;
33
- }
34
- function getDeepName(node) {
35
- let current = node;
36
- let name = "";
37
- while (ts_morph_1.Node.isPropertyAccessExpression(current)) {
38
- name =
39
- current.getName();
40
- current =
41
- current.getExpression();
42
- }
43
- return name;
44
- }
45
- function extractEventFromArgs(call, event) {
46
- const args = call.getArguments();
47
- const events = [];
48
- for (let i = 0; i < args.length; i++) {
49
- const arg = args[i];
50
- // track("event")
51
- if (!event) {
52
- const result = (0, extract_1.extractExpression)(arg);
53
- if (!result)
54
- continue;
55
- result.values.forEach((value) => events.push({
56
- value,
57
- dynamic: result.dynamic
58
- }));
59
- }
60
- // track({event})
61
- if (event) {
62
- const obj = arg.asKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
63
- if (!obj)
64
- continue;
65
- const prop = obj.getProperty(event);
66
- if (!prop)
67
- continue;
68
- if (ts_morph_1.Node.isPropertyAssignment(prop)) {
69
- const init = prop.getInitializer();
70
- if (!init)
71
- continue;
72
- const result = (0, extract_1.extractExpression)(init);
73
- if (!result)
74
- continue;
75
- result.values.forEach((value) => events.push({
76
- value,
77
- dynamic: result.dynamic
78
- }));
79
- }
80
- }
81
- }
82
- return events.length
83
- ? events
84
- : null;
85
- }
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scanTrack = scanTrack;
4
- const ts_morph_1 = require("ts-morph");
5
- const extract_1 = require("../extract");
6
- function scanTrack(source, config) {
7
- const events = new Set();
8
- const calls = source.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
9
- for (const call of calls) {
10
- const expr = call.getExpression();
11
- let isTrack = false;
12
- // track()
13
- if (ts_morph_1.Node.isIdentifier(expr)) {
14
- isTrack = expr.getText() === "track";
15
- }
16
- // analytics.track()
17
- if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
18
- isTrack = expr.getName() === "track";
19
- }
20
- if (!isTrack)
21
- continue;
22
- const func = call.getFirstAncestorByKind(ts_morph_1.SyntaxKind.FunctionDeclaration) ||
23
- call.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ArrowFunction) ||
24
- call.getFirstAncestorByKind(ts_morph_1.SyntaxKind.FunctionExpression);
25
- let fnName;
26
- if (func) {
27
- if (ts_morph_1.Node.isFunctionDeclaration(func)) {
28
- fnName = func.getName();
29
- }
30
- if (ts_morph_1.Node.isArrowFunction(func) ||
31
- ts_morph_1.Node.isFunctionExpression(func)) {
32
- const varDecl = func.getFirstAncestorByKind(ts_morph_1.SyntaxKind.VariableDeclaration);
33
- fnName = varDecl?.getName();
34
- }
35
- }
36
- if (fnName &&
37
- config.functionWrappers?.some((w) => w.name === fnName)) {
38
- continue;
39
- }
40
- const arg = call.getArguments()[0];
41
- if (!arg)
42
- continue;
43
- const result = (0, extract_1.extractExpression)(arg);
44
- if (!result)
45
- continue;
46
- result.values.forEach((value) => events.add({
47
- value,
48
- dynamic: result.dynamic,
49
- }));
50
- }
51
- return events;
52
- }
@@ -1,185 +0,0 @@
1
- import {
2
- Node,
3
- SyntaxKind
4
- } from "ts-morph";
5
-
6
- export type ExtractResult = {
7
- values: string[];
8
- dynamic: boolean;
9
- };
10
-
11
- export function extractExpression(
12
- expr: Node
13
- ): ExtractResult | null {
14
-
15
- // "event"
16
- if (Node.isStringLiteral(expr)) {
17
- return {
18
- values: [expr.getLiteralText()],
19
- dynamic: false
20
- };
21
- }
22
-
23
- // `event`
24
- if (
25
- expr.getKind() ===
26
- SyntaxKind.NoSubstitutionTemplateLiteral
27
- ) {
28
- return {
29
- values: [
30
- expr.getText().replace(/`/g, "")
31
- ],
32
- dynamic: false
33
- };
34
- }
35
-
36
- // identifier (TEST)
37
- if (Node.isIdentifier(expr)) {
38
- const name = expr.getText();
39
-
40
- const defs =
41
- "getDefinitions" in expr
42
- ? (expr as any).getDefinitions()
43
- : [];
44
-
45
- if (defs && defs.length > 0) {
46
- const decl = defs[0]?.getDeclarationNode();
47
-
48
- if (decl && Node.isVariableDeclaration(decl)) {
49
- const initializer = decl.getInitializer();
50
-
51
- // const TEST = "event"
52
- if (
53
- initializer &&
54
- Node.isStringLiteral(initializer)
55
- ) {
56
- return {
57
- values: [initializer.getLiteralText()],
58
- dynamic: false
59
- };
60
- }
61
-
62
- // const TEST = `event`
63
- if (
64
- initializer &&
65
- initializer.getKind() ===
66
- SyntaxKind.NoSubstitutionTemplateLiteral
67
- ) {
68
- return {
69
- values: [
70
- initializer.getText().replace(/`/g, "")
71
- ],
72
- dynamic: false
73
- };
74
- }
75
- }
76
- }
77
-
78
- return {
79
- values: [name],
80
- dynamic: true
81
- };
82
- }
83
-
84
- // property access (EVENTS.CLICK)
85
- if (Node.isPropertyAccessExpression(expr)) {
86
- const fullName = expr.getText(); // EVENTS.CLICK
87
- const propName = expr.getName(); // CLICK
88
-
89
- const defs =
90
- "getDefinitions" in expr
91
- ? (expr as any).getDefinitions()
92
- : [];
93
-
94
- if (defs && defs.length > 0) {
95
- const decl = defs[0]?.getDeclarationNode();
96
-
97
- // const EVENTS = { CLICK: "event" }
98
- if (decl && Node.isVariableDeclaration(decl)) {
99
- const init = decl.getInitializer();
100
-
101
- if (init && Node.isObjectLiteralExpression(init)) {
102
- const prop = init.getProperty(propName);
103
-
104
- if (
105
- prop &&
106
- Node.isPropertyAssignment(prop)
107
- ) {
108
- const value = prop.getInitializer();
109
-
110
- if (
111
- value &&
112
- Node.isStringLiteral(value)
113
- ) {
114
- return {
115
- values: [value.getLiteralText()],
116
- dynamic: false
117
- };
118
- }
119
- }
120
- }
121
- }
122
-
123
- // fallback
124
- if (decl && Node.isPropertyAssignment(decl)) {
125
- const initializer = decl.getInitializer();
126
-
127
- if (
128
- initializer &&
129
- Node.isStringLiteral(initializer)
130
- ) {
131
- return {
132
- values: [initializer.getLiteralText()],
133
- dynamic: false
134
- };
135
- }
136
- }
137
- }
138
-
139
- return {
140
- values: [fullName],
141
- dynamic: true
142
- };
143
- }
144
-
145
- // ternary (a ? b : c)
146
- if (Node.isConditionalExpression(expr)) {
147
- const values: string[] = [];
148
-
149
- const whenTrue =
150
- extractExpression(expr.getWhenTrue());
151
-
152
- const whenFalse =
153
- extractExpression(expr.getWhenFalse());
154
-
155
- if (whenTrue) values.push(...whenTrue.values);
156
- if (whenFalse) values.push(...whenFalse.values);
157
-
158
- return {
159
- values,
160
- dynamic: true
161
- };
162
- }
163
-
164
- // template `${}`
165
- if (Node.isTemplateExpression(expr)) {
166
- return {
167
- values: [
168
- expr.getText().replace(/`/g, "")
169
- ],
170
- dynamic: true
171
- };
172
- }
173
-
174
- // call (fn())
175
- if (Node.isCallExpression(expr)) {
176
- return {
177
- values: [
178
- expr.getExpression().getText()
179
- ],
180
- dynamic: true
181
- };
182
- }
183
-
184
- return null;
185
- }
@@ -1,107 +0,0 @@
1
- import {
2
- SourceFile,
3
- SyntaxKind,
4
- Node
5
- } from "ts-morph";
6
-
7
- import {
8
- ComponentWrapper,
9
- ExtractedEvent
10
- } from "../../types";
11
-
12
- import { extractExpression } from "../extract";
13
-
14
- export function scanComponentWrappers(
15
- source: SourceFile,
16
- wrappers: ComponentWrapper[]
17
- ) {
18
- const events = new Set<ExtractedEvent>();
19
-
20
- const elements = [
21
- ...source.getDescendantsOfKind(
22
- SyntaxKind.JsxOpeningElement
23
- ),
24
- ...source.getDescendantsOfKind(
25
- SyntaxKind.JsxSelfClosingElement
26
- )
27
- ];
28
-
29
- for (const el of elements) {
30
- const name = el
31
- .getTagNameNode()
32
- .getText()
33
- .toLowerCase();
34
-
35
- for (const wrapper of wrappers) {
36
- if (name !== wrapper.name.toLowerCase())
37
- continue;
38
-
39
- const attrs = el.getAttributes();
40
-
41
- for (const attr of attrs) {
42
-
43
- // ❗ NO spread props
44
- if (Node.isJsxSpreadAttribute(attr)) {
45
- continue;
46
- }
47
-
48
- const attrNode =
49
- attr.asKind(SyntaxKind.JsxAttribute);
50
-
51
- if (!attrNode) continue;
52
-
53
- const key =
54
- attrNode
55
- .getNameNode()
56
- .getText()
57
- .toLowerCase();
58
-
59
- if (key !== wrapper.prop.toLowerCase())
60
- continue;
61
-
62
- const init = attrNode.getInitializer();
63
-
64
- // <Button event />
65
- if (!init) {
66
- events.add({
67
- value: key,
68
- dynamic: true
69
- });
70
- continue;
71
- }
72
-
73
- // string: <Button event="click" />
74
- if (Node.isStringLiteral(init)) {
75
- events.add({
76
- value: init.getLiteralText(),
77
- dynamic: false
78
- });
79
- continue;
80
- }
81
-
82
- // jsx expression: <Button event={"click"} />
83
- if (Node.isJsxExpression(init)) {
84
- const expr = init.getExpression();
85
-
86
- if (!expr) continue;
87
-
88
- const result =
89
- extractExpression(expr);
90
-
91
- if (!result) continue;
92
-
93
- result.values.forEach((value) =>
94
- events.add({
95
- value,
96
- dynamic: result.dynamic
97
- })
98
- );
99
-
100
- continue;
101
- }
102
- }
103
- }
104
- }
105
-
106
- return events;
107
- }