@eventra_dev/eventra-cli 0.0.4 → 0.0.6
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/.github/workflows/release.yml +3 -0
- package/README.md +125 -44
- package/dist/commands/init.js +4 -1
- package/dist/commands/send.js +41 -7
- package/dist/commands/sync.js +35 -194
- package/dist/index.js +0 -0
- package/dist/types.js +2 -0
- package/dist/utils/config.js +4 -1
- package/dist/utils/extract.js +44 -0
- package/dist/utils/parsers/astro.js +6 -0
- package/dist/utils/parsers/router.js +12 -0
- package/dist/utils/parsers/svelte.js +6 -0
- package/dist/utils/parsers/vue.js +18 -0
- package/dist/utils/scanners/component-wrappers.js +55 -0
- package/dist/utils/scanners/function-wrappers.js +45 -0
- package/dist/utils/scanners/track.js +42 -0
- package/package.json +6 -4
- package/src/commands/init.ts +4 -1
- package/src/commands/send.ts +101 -17
- package/src/commands/sync.ts +66 -345
- package/src/types.ts +20 -0
- package/src/utils/config.ts +24 -7
- package/src/utils/extract.ts +74 -0
- package/src/utils/parsers/astro.ts +5 -0
- package/src/utils/parsers/router.ts +14 -0
- package/src/utils/parsers/svelte.ts +5 -0
- package/src/utils/parsers/vue.ts +25 -0
- package/src/utils/scanners/component-wrappers.ts +108 -0
- package/src/utils/scanners/function-wrappers.ts +98 -0
- package/src/utils/scanners/track.ts +84 -0
- package/tests/fixtures/backend/express/app.ts +78 -0
- package/tests/fixtures/backend/nest/service.ts +70 -0
- package/tests/fixtures/backend/node/index.ts +63 -0
- package/tests/fixtures/frontend/next/page.tsx +101 -0
- package/tests/fixtures/frontend/react/App.tsx +104 -0
- package/tests/fixtures/frontend/vue/App.vue +83 -0
- package/tests/fixtures/wrappers/component/test.tsx +62 -0
- package/tests/fixtures/wrappers/function/test.ts +60 -0
- package/tests/run.ts +120 -0
- package/tsconfig.json +2 -0
package/README.md
CHANGED
|
@@ -4,69 +4,70 @@
|
|
|
4
4
|
|
|
5
5
|
# Eventra CLI
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/@eventra_dev/eventra-cli)
|
|
7
|
+
[](https://www.npmjs.com/package/@eventra_dev/eventra-cli)
|
|
8
|
+
[](https://www.npmjs.com/package/@eventra_dev/eventra-cli)
|
|
11
9
|
[](https://www.typescriptlang.org/)
|
|
10
|
+
[]()
|
|
11
|
+
[]()
|
|
12
12
|
|
|
13
|
-
Eventra CLI automatically discovers feature usage events in your
|
|
14
|
-
codebase and syncs them with Eventra.
|
|
13
|
+
Eventra CLI automatically discovers feature usage events in your codebase and syncs them with Eventra.
|
|
15
14
|
|
|
16
15
|
Eventra CLI helps you:
|
|
17
16
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
17
|
+
- Discover feature usage automatically
|
|
18
|
+
- Detect wrapper components
|
|
19
|
+
- Detect wrapper functions
|
|
20
|
+
- Keep events in sync
|
|
21
|
+
- Register features in Eventra
|
|
22
|
+
- Maintain consistent event naming
|
|
23
23
|
|
|
24
24
|
It is designed to be:
|
|
25
25
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
26
|
+
- framework-agnostic
|
|
27
|
+
- static analysis based
|
|
28
|
+
- zero runtime overhead
|
|
29
|
+
- production-safe
|
|
30
|
+
- backend + frontend compatible
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
---
|
|
32
33
|
|
|
33
34
|
# Installation
|
|
34
35
|
|
|
35
36
|
### npm
|
|
36
37
|
|
|
37
|
-
```
|
|
38
|
+
```bash
|
|
38
39
|
npm install -D @eventra_dev/eventra-cli
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
### pnpm
|
|
42
43
|
|
|
43
|
-
```
|
|
44
|
+
```bash
|
|
44
45
|
pnpm add -D @eventra_dev/eventra-cli
|
|
45
46
|
```
|
|
46
47
|
|
|
47
48
|
### yarn
|
|
48
49
|
|
|
49
|
-
```
|
|
50
|
+
```bash
|
|
50
51
|
yarn add -D @eventra_dev/eventra-cli
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
### npx
|
|
54
55
|
|
|
55
|
-
```
|
|
56
|
+
```bash
|
|
56
57
|
npx eventra init
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
---
|
|
60
61
|
|
|
61
62
|
# Quick Start
|
|
62
63
|
|
|
63
|
-
```
|
|
64
|
+
```bash
|
|
64
65
|
eventra init
|
|
65
66
|
eventra sync
|
|
66
67
|
eventra send
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
---
|
|
70
71
|
|
|
71
72
|
# Commands
|
|
72
73
|
|
|
@@ -74,60 +75,140 @@ eventra send
|
|
|
74
75
|
|
|
75
76
|
Creates `eventra.json` configuration file.
|
|
76
77
|
|
|
77
|
-
```
|
|
78
|
+
```bash
|
|
78
79
|
eventra init
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
---
|
|
82
83
|
|
|
83
84
|
## eventra sync
|
|
84
85
|
|
|
85
86
|
Scans your project and discovers events automatically.
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
### track() detection
|
|
89
|
+
|
|
90
|
+
```ts
|
|
88
91
|
tracker.track("feature_created")
|
|
89
92
|
```
|
|
90
93
|
|
|
91
|
-
```
|
|
94
|
+
```ts
|
|
95
|
+
track("user_signup")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Component wrappers
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
92
103
|
<TrackedButton event="feature_created" />
|
|
93
104
|
```
|
|
94
105
|
|
|
95
|
-
```
|
|
106
|
+
```tsx
|
|
96
107
|
<MyComponent event="user_signup" />
|
|
97
108
|
```
|
|
98
109
|
|
|
99
|
-
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Function wrappers
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
trackFeature("feature_created")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
analytics.trackFeature("user_signup")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
100
123
|
|
|
101
124
|
## eventra send
|
|
102
125
|
|
|
103
126
|
Send events to Eventra backend.
|
|
104
127
|
|
|
105
|
-
```
|
|
128
|
+
```bash
|
|
106
129
|
eventra send
|
|
107
130
|
```
|
|
108
131
|
|
|
109
|
-
|
|
132
|
+
New events are queued for processing and will appear in dashboard shortly.
|
|
133
|
+
|
|
134
|
+
Processing typically takes:
|
|
135
|
+
|
|
136
|
+
~1-2 minutes
|
|
137
|
+
|
|
138
|
+
This delay ensures reliable event ingestion and aggregation.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# Configuration
|
|
143
|
+
|
|
144
|
+
Eventra CLI creates `eventra.json`
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"apiKey": "",
|
|
151
|
+
"events": [],
|
|
152
|
+
"wrappers": [],
|
|
153
|
+
"functionWrappers": [],
|
|
154
|
+
"sync": {
|
|
155
|
+
"include": ["**/*.{ts,tsx,js,jsx}"],
|
|
156
|
+
"exclude": [
|
|
157
|
+
"node_modules",
|
|
158
|
+
"dist",
|
|
159
|
+
".next",
|
|
160
|
+
".git"
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
110
167
|
|
|
111
168
|
# Supported Frameworks
|
|
112
169
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
|
|
170
|
+
Frontend:
|
|
171
|
+
|
|
172
|
+
- React
|
|
173
|
+
- Next.js
|
|
174
|
+
- Vue
|
|
175
|
+
- Nuxt
|
|
176
|
+
- Svelte
|
|
177
|
+
- Astro
|
|
178
|
+
|
|
179
|
+
Backend:
|
|
180
|
+
|
|
181
|
+
- Node.js
|
|
182
|
+
- Express
|
|
183
|
+
- NestJS
|
|
184
|
+
- Fastify
|
|
185
|
+
- Hono
|
|
186
|
+
- Bun
|
|
187
|
+
- Deno
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
# How It Works
|
|
192
|
+
|
|
193
|
+
Eventra CLI:
|
|
194
|
+
|
|
195
|
+
1. Scans your codebase
|
|
196
|
+
2. Detects tracking calls
|
|
197
|
+
3. Detects wrapper components
|
|
198
|
+
4. Detects wrapper functions
|
|
199
|
+
5. Syncs discovered events
|
|
200
|
+
6. Registers events in Eventra
|
|
201
|
+
|
|
202
|
+
No runtime SDK required.
|
|
122
203
|
|
|
123
|
-
|
|
204
|
+
---
|
|
124
205
|
|
|
125
206
|
# Requirements
|
|
126
207
|
|
|
127
|
-
-
|
|
128
|
-
-
|
|
208
|
+
- Node.js 18+
|
|
209
|
+
- JavaScript / TypeScript project
|
|
129
210
|
|
|
130
|
-
|
|
211
|
+
---
|
|
131
212
|
|
|
132
213
|
# License
|
|
133
214
|
|
package/dist/commands/init.js
CHANGED
|
@@ -27,8 +27,11 @@ async function init() {
|
|
|
27
27
|
apiKey: answers.apiKey || "",
|
|
28
28
|
events: [],
|
|
29
29
|
wrappers: [],
|
|
30
|
+
functionWrappers: [],
|
|
30
31
|
sync: {
|
|
31
|
-
include: [
|
|
32
|
+
include: [
|
|
33
|
+
"**/*.{ts,tsx,js,jsx,vue,svelte,astro}"
|
|
34
|
+
],
|
|
32
35
|
exclude: [
|
|
33
36
|
"node_modules",
|
|
34
37
|
"dist",
|
package/dist/commands/send.js
CHANGED
|
@@ -7,15 +7,17 @@ exports.send = send;
|
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
9
|
const config_1 = require("../utils/config");
|
|
10
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
10
11
|
const EVENTRA_ENDPOINT = process.env.EVENTRA_ENDPOINT ?? "";
|
|
11
|
-
const CLI_VERSION =
|
|
12
|
+
const CLI_VERSION = package_json_1.default.version;
|
|
12
13
|
async function send() {
|
|
13
|
-
|
|
14
|
+
const config = await (0, config_1.loadConfig)();
|
|
14
15
|
if (!config) {
|
|
15
16
|
console.log(chalk_1.default.red("eventra.json not found. Run 'eventra init'"));
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
let apiKey = config.apiKey;
|
|
20
|
+
// ask api key
|
|
19
21
|
if (!apiKey) {
|
|
20
22
|
const answers = await inquirer_1.default.prompt([
|
|
21
23
|
{
|
|
@@ -29,11 +31,21 @@ async function send() {
|
|
|
29
31
|
await (0, config_1.saveConfig)(config);
|
|
30
32
|
console.log(chalk_1.default.green("API key saved"));
|
|
31
33
|
}
|
|
32
|
-
|
|
34
|
+
// no events
|
|
35
|
+
if (!config.events?.length) {
|
|
33
36
|
console.log(chalk_1.default.yellow("No events found. Run 'eventra sync'"));
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
|
-
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
console.log(chalk_1.default.red("API key required"));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!EVENTRA_ENDPOINT) {
|
|
44
|
+
console.log(chalk_1.default.red("EVENTRA_ENDPOINT not configured"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log(chalk_1.default.blue(`Sending ${config.events.length} events...`));
|
|
37
49
|
try {
|
|
38
50
|
const res = await fetch(EVENTRA_ENDPOINT, {
|
|
39
51
|
method: "POST",
|
|
@@ -50,18 +62,40 @@ async function send() {
|
|
|
50
62
|
}
|
|
51
63
|
})
|
|
52
64
|
});
|
|
53
|
-
if (res.
|
|
54
|
-
console.log(chalk_1.default.red(`
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
console.log(chalk_1.default.red(`Request failed (${res.status})`));
|
|
67
|
+
try {
|
|
68
|
+
const text = await res.text();
|
|
69
|
+
console.log(chalk_1.default.gray(text));
|
|
70
|
+
}
|
|
71
|
+
catch { }
|
|
55
72
|
return;
|
|
56
73
|
}
|
|
57
74
|
const data = await res.json();
|
|
58
75
|
console.log(chalk_1.default.green("Events registered successfully"));
|
|
76
|
+
// created
|
|
59
77
|
if (data.created?.length) {
|
|
60
78
|
console.log(chalk_1.default.green("\nNew events:"));
|
|
61
79
|
data.created.forEach((e) => console.log(chalk_1.default.green(`+ ${e}`)));
|
|
62
80
|
}
|
|
81
|
+
// existing
|
|
82
|
+
if (data.existing?.length) {
|
|
83
|
+
console.log(chalk_1.default.gray("\nExisting events:"));
|
|
84
|
+
data.existing.forEach((e) => console.log(chalk_1.default.gray(`• ${e}`)));
|
|
85
|
+
}
|
|
86
|
+
// processing notice
|
|
87
|
+
if (data.created?.length) {
|
|
88
|
+
console.log("");
|
|
89
|
+
console.log(chalk_1.default.yellow("Events queued for processing (~2 min)"));
|
|
90
|
+
console.log(chalk_1.default.gray("They will appear in dashboard shortly"));
|
|
91
|
+
}
|
|
92
|
+
console.log("");
|
|
93
|
+
console.log(chalk_1.default.gray(`Sent ${config.events.length} events`));
|
|
63
94
|
}
|
|
64
|
-
catch {
|
|
95
|
+
catch (err) {
|
|
65
96
|
console.log(chalk_1.default.red("Network error"));
|
|
97
|
+
if (err instanceof Error) {
|
|
98
|
+
console.log(chalk_1.default.gray(err.message));
|
|
99
|
+
}
|
|
66
100
|
}
|
|
67
101
|
}
|
package/dist/commands/sync.js
CHANGED
|
@@ -4,213 +4,54 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.sync = sync;
|
|
7
|
-
const ts_morph_1 = require("ts-morph");
|
|
8
|
-
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const
|
|
8
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
|
+
const ts_morph_1 = require("ts-morph");
|
|
10
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
11
|
const config_1 = require("../utils/config");
|
|
12
|
+
const router_1 = require("../utils/parsers/router");
|
|
13
|
+
const vue_1 = require("../utils/parsers/vue");
|
|
14
|
+
const svelte_1 = require("../utils/parsers/svelte");
|
|
15
|
+
const astro_1 = require("../utils/parsers/astro");
|
|
16
|
+
const track_1 = require("../utils/scanners/track");
|
|
17
|
+
const function_wrappers_1 = require("../utils/scanners/function-wrappers");
|
|
18
|
+
const component_wrappers_1 = require("../utils/scanners/component-wrappers");
|
|
12
19
|
async function sync() {
|
|
13
|
-
|
|
20
|
+
const config = await (0, config_1.loadConfig)();
|
|
14
21
|
if (!config) {
|
|
15
|
-
console.log(chalk_1.default.red("Run 'eventra init'
|
|
22
|
+
console.log(chalk_1.default.red("Run 'eventra init'"));
|
|
16
23
|
return;
|
|
17
24
|
}
|
|
18
|
-
const events = new Set();
|
|
19
|
-
const project = new ts_morph_1.Project();
|
|
20
25
|
console.log(chalk_1.default.blue("Scanning project..."));
|
|
26
|
+
const project = new ts_morph_1.Project();
|
|
27
|
+
const events = new Set();
|
|
21
28
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
22
29
|
ignore: config.sync.exclude
|
|
23
30
|
});
|
|
24
|
-
|
|
31
|
+
const functionWrappers = (config.functionWrappers ?? []).map((w) => ({
|
|
32
|
+
...w,
|
|
33
|
+
path: w.path ?? "0"
|
|
34
|
+
}));
|
|
35
|
+
const componentWrappers = config.wrappers ?? [];
|
|
25
36
|
for (const file of files) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let event = null;
|
|
42
|
-
// "event"
|
|
43
|
-
if (eventArg.getKind() ===
|
|
44
|
-
ts_morph_1.SyntaxKind.StringLiteral) {
|
|
45
|
-
event =
|
|
46
|
-
eventArg
|
|
47
|
-
.asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral)
|
|
48
|
-
.getLiteralText();
|
|
49
|
-
}
|
|
50
|
-
// `event`
|
|
51
|
-
if (eventArg.getKind() ===
|
|
52
|
-
ts_morph_1.SyntaxKind.NoSubstitutionTemplateLiteral) {
|
|
53
|
-
event =
|
|
54
|
-
eventArg
|
|
55
|
-
.asKindOrThrow(ts_morph_1.SyntaxKind.NoSubstitutionTemplateLiteral)
|
|
56
|
-
.getLiteralText();
|
|
57
|
-
}
|
|
58
|
-
if (event) {
|
|
59
|
-
events.add(event);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
console.log(chalk_1.default.green(`Found ${events.size} track events`));
|
|
65
|
-
// wrappers setup
|
|
66
|
-
if (!config.wrappers.length) {
|
|
67
|
-
const { useWrapper } = await inquirer_1.default.prompt([
|
|
68
|
-
{
|
|
69
|
-
type: "confirm",
|
|
70
|
-
name: "useWrapper",
|
|
71
|
-
message: "Use wrapper components?",
|
|
72
|
-
default: true
|
|
73
|
-
}
|
|
74
|
-
]);
|
|
75
|
-
if (useWrapper) {
|
|
76
|
-
const wrappers = [];
|
|
77
|
-
let addMore = true;
|
|
78
|
-
while (addMore) {
|
|
79
|
-
const answers = await inquirer_1.default.prompt([
|
|
80
|
-
{
|
|
81
|
-
type: "input",
|
|
82
|
-
name: "name",
|
|
83
|
-
message: "Wrapper component name:"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: "input",
|
|
87
|
-
name: "prop",
|
|
88
|
-
message: "Event prop name:"
|
|
89
|
-
}
|
|
90
|
-
]);
|
|
91
|
-
wrappers.push({
|
|
92
|
-
name: answers.name,
|
|
93
|
-
prop: answers.prop
|
|
94
|
-
});
|
|
95
|
-
const more = await inquirer_1.default.prompt([
|
|
96
|
-
{
|
|
97
|
-
type: "confirm",
|
|
98
|
-
name: "more",
|
|
99
|
-
message: "Add another wrapper?",
|
|
100
|
-
default: false
|
|
101
|
-
}
|
|
102
|
-
]);
|
|
103
|
-
addMore = more.more;
|
|
104
|
-
}
|
|
105
|
-
config.wrappers = wrappers;
|
|
106
|
-
}
|
|
37
|
+
const parser = (0, router_1.detectParser)(file);
|
|
38
|
+
let content = await promises_1.default.readFile(file, "utf-8");
|
|
39
|
+
if (parser === "vue")
|
|
40
|
+
content = (0, vue_1.parseVue)(content);
|
|
41
|
+
if (parser === "svelte")
|
|
42
|
+
content = (0, svelte_1.parseSvelte)(content);
|
|
43
|
+
if (parser === "astro")
|
|
44
|
+
content = (0, astro_1.parseAstro)(content);
|
|
45
|
+
const virtualFile = parser === "ts"
|
|
46
|
+
? file
|
|
47
|
+
: file + ".tsx";
|
|
48
|
+
const source = project.createSourceFile(virtualFile, content, { overwrite: true });
|
|
49
|
+
(0, track_1.scanTrack)(source).forEach((e) => events.add(e));
|
|
50
|
+
(0, function_wrappers_1.scanFunctionWrappers)(source, functionWrappers).forEach((e) => events.add(e));
|
|
51
|
+
(0, component_wrappers_1.scanComponentWrappers)(source, componentWrappers).forEach((e) => events.add(e));
|
|
107
52
|
}
|
|
108
|
-
// scan wrappers
|
|
109
|
-
if (config.wrappers.length) {
|
|
110
|
-
console.log(chalk_1.default.blue("Scanning wrappers..."));
|
|
111
|
-
for (const file of files) {
|
|
112
|
-
const sourceFile = project.addSourceFileAtPath(file);
|
|
113
|
-
const elements = [
|
|
114
|
-
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxOpeningElement),
|
|
115
|
-
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement)
|
|
116
|
-
];
|
|
117
|
-
for (const element of elements) {
|
|
118
|
-
const tagName = element
|
|
119
|
-
.getTagNameNode()
|
|
120
|
-
.getText()
|
|
121
|
-
.toLowerCase();
|
|
122
|
-
for (const wrapper of config.wrappers) {
|
|
123
|
-
if (tagName !==
|
|
124
|
-
wrapper.name.toLowerCase())
|
|
125
|
-
continue;
|
|
126
|
-
const attrs = element.getAttributes();
|
|
127
|
-
for (const attr of attrs) {
|
|
128
|
-
if (attr.getKind() !==
|
|
129
|
-
ts_morph_1.SyntaxKind.JsxAttribute)
|
|
130
|
-
continue;
|
|
131
|
-
const attrNode = attr.asKind(ts_morph_1.SyntaxKind.JsxAttribute);
|
|
132
|
-
if (!attrNode)
|
|
133
|
-
continue;
|
|
134
|
-
const attrName = attrNode
|
|
135
|
-
.getNameNode()
|
|
136
|
-
.getText()
|
|
137
|
-
.toLowerCase();
|
|
138
|
-
if (attrName !==
|
|
139
|
-
wrapper.prop.toLowerCase())
|
|
140
|
-
continue;
|
|
141
|
-
const initializer = attrNode.getInitializer();
|
|
142
|
-
if (!initializer)
|
|
143
|
-
continue;
|
|
144
|
-
let value = null;
|
|
145
|
-
// event="signup"
|
|
146
|
-
if (initializer.getKind() ===
|
|
147
|
-
ts_morph_1.SyntaxKind.StringLiteral) {
|
|
148
|
-
value =
|
|
149
|
-
initializer
|
|
150
|
-
.asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral)
|
|
151
|
-
.getLiteralText();
|
|
152
|
-
}
|
|
153
|
-
// event={"signup"}
|
|
154
|
-
if (initializer.getKind() ===
|
|
155
|
-
ts_morph_1.SyntaxKind.JsxExpression) {
|
|
156
|
-
const expr = initializer
|
|
157
|
-
.asKindOrThrow(ts_morph_1.SyntaxKind.JsxExpression)
|
|
158
|
-
.getExpression();
|
|
159
|
-
if (expr?.getKind() ===
|
|
160
|
-
ts_morph_1.SyntaxKind.StringLiteral) {
|
|
161
|
-
value =
|
|
162
|
-
expr
|
|
163
|
-
.asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral)
|
|
164
|
-
.getLiteralText();
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (value) {
|
|
168
|
-
events.add(value);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
// results
|
|
176
53
|
const list = [...events].sort();
|
|
177
|
-
console.log("");
|
|
178
|
-
console.log(chalk_1.default.green("Found events:"));
|
|
179
|
-
list.forEach((e) => console.log(chalk_1.default.gray(`- ${e}`)));
|
|
180
|
-
console.log("");
|
|
181
|
-
// diff
|
|
182
|
-
const previous = config.events ?? [];
|
|
183
|
-
const added = list.filter((e) => !previous.includes(e));
|
|
184
|
-
const removed = previous.filter((e) => !list.includes(e));
|
|
185
|
-
if (added.length || removed.length) {
|
|
186
|
-
console.log(chalk_1.default.blue("Changes:"));
|
|
187
|
-
if (added.length) {
|
|
188
|
-
console.log(chalk_1.default.green("New events:"));
|
|
189
|
-
added.forEach((e) => console.log(chalk_1.default.green(`+ ${e}`)));
|
|
190
|
-
}
|
|
191
|
-
if (removed.length) {
|
|
192
|
-
console.log(chalk_1.default.red("Removed events:"));
|
|
193
|
-
removed.forEach((e) => console.log(chalk_1.default.red(`- ${e}`)));
|
|
194
|
-
}
|
|
195
|
-
console.log("");
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
console.log(chalk_1.default.gray("No changes detected"));
|
|
199
|
-
}
|
|
200
|
-
// confirm
|
|
201
|
-
const { confirm } = await inquirer_1.default.prompt([
|
|
202
|
-
{
|
|
203
|
-
type: "confirm",
|
|
204
|
-
name: "confirm",
|
|
205
|
-
message: "Sync these events?",
|
|
206
|
-
default: true
|
|
207
|
-
}
|
|
208
|
-
]);
|
|
209
|
-
if (!confirm) {
|
|
210
|
-
console.log(chalk_1.default.yellow("Sync cancelled"));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
54
|
config.events = list;
|
|
214
55
|
await (0, config_1.saveConfig)(config);
|
|
215
|
-
console.log(chalk_1.default.green(
|
|
56
|
+
console.log(chalk_1.default.green(`Found ${list.length} events`));
|
|
216
57
|
}
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/types.js
ADDED
package/dist/utils/config.js
CHANGED
|
@@ -15,8 +15,11 @@ function normalizeConfig(config) {
|
|
|
15
15
|
apiKey: config.apiKey ?? "",
|
|
16
16
|
events: config.events ?? [],
|
|
17
17
|
wrappers: config.wrappers ?? [],
|
|
18
|
+
functionWrappers: config.functionWrappers ?? [],
|
|
18
19
|
sync: config.sync ?? {
|
|
19
|
-
include: [
|
|
20
|
+
include: [
|
|
21
|
+
"**/*.{ts,tsx,js,jsx,vue,svelte,astro}"
|
|
22
|
+
],
|
|
20
23
|
exclude: [
|
|
21
24
|
"node_modules",
|
|
22
25
|
"dist",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractEvent = extractEvent;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
function extractEvent(call, path) {
|
|
6
|
+
const parts = path.split(".");
|
|
7
|
+
let node = call.getArguments()[Number(parts[0])];
|
|
8
|
+
if (!node)
|
|
9
|
+
return null;
|
|
10
|
+
node = unwrap(node);
|
|
11
|
+
for (let i = 1; i < parts.length; i++) {
|
|
12
|
+
if (!ts_morph_1.Node.isObjectLiteralExpression(node)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const obj = node;
|
|
16
|
+
const prop = obj.getProperty(parts[i]);
|
|
17
|
+
if (!prop)
|
|
18
|
+
return null;
|
|
19
|
+
if (!ts_morph_1.Node.isPropertyAssignment(prop)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const initializer = prop.getInitializer();
|
|
23
|
+
if (!initializer)
|
|
24
|
+
return null;
|
|
25
|
+
node = unwrap(initializer);
|
|
26
|
+
}
|
|
27
|
+
if (ts_morph_1.Node.isStringLiteral(node)) {
|
|
28
|
+
return node.getLiteralText();
|
|
29
|
+
}
|
|
30
|
+
if (node.getKind() ===
|
|
31
|
+
ts_morph_1.SyntaxKind.NoSubstitutionTemplateLiteral) {
|
|
32
|
+
return node
|
|
33
|
+
.getText()
|
|
34
|
+
.replace(/`/g, "");
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function unwrap(node) {
|
|
39
|
+
let current = node;
|
|
40
|
+
while (ts_morph_1.Node.isParenthesizedExpression(current)) {
|
|
41
|
+
current = current.getExpression();
|
|
42
|
+
}
|
|
43
|
+
return current;
|
|
44
|
+
}
|