@eventra_dev/eventra-cli 0.0.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/.github/workflows/release.yml +40 -0
- package/LICENSE +9 -0
- package/README.md +310 -0
- package/dist/commands/init.js +42 -0
- package/dist/commands/send.js +67 -0
- package/dist/commands/sync.js +216 -0
- package/dist/index.js +64 -0
- package/dist/utils/config.js +43 -0
- package/package.json +30 -0
- package/src/commands/init.ts +60 -0
- package/src/commands/send.ts +124 -0
- package/src/commands/sync.ts +397 -0
- package/src/index.ts +36 -0
- package/src/utils/config.ts +53 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Release CLI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: npm-publish
|
|
9
|
+
cancel-in-progress: true
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: pnpm/action-setup@v4
|
|
23
|
+
with:
|
|
24
|
+
version: 10
|
|
25
|
+
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 20
|
|
29
|
+
registry-url: https://registry.npmjs.org/
|
|
30
|
+
|
|
31
|
+
- run: pnpm install --no-frozen-lockfile
|
|
32
|
+
|
|
33
|
+
- name: Build CLI
|
|
34
|
+
run: pnpm build
|
|
35
|
+
|
|
36
|
+
- name: Publish to npm
|
|
37
|
+
run: npm publish --access public
|
|
38
|
+
env:
|
|
39
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
40
|
+
EVENTRA_ENDPOINT: ${{ secrets.EVENTRA_ENDPOINT }}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eventra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software.
|
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Eventra CLI
|
|
2
|
+
|
|
3
|
+
Eventra CLI automatically discovers feature usage events in your codebase and syncs them with Eventra.
|
|
4
|
+
|
|
5
|
+
It helps you:
|
|
6
|
+
|
|
7
|
+
- Automatically discover events
|
|
8
|
+
- Detect wrapper components
|
|
9
|
+
- Keep events in sync
|
|
10
|
+
- Register features in Eventra
|
|
11
|
+
- Works with any JS framework (React, Vue, Svelte, Node, etc.)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Installation
|
|
16
|
+
|
|
17
|
+
Using npm
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -D @eventra_dev/eventra-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Using pnpm
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -D @eventra_dev/eventra-cli
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Using npx
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx eventra init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# Quick Start
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
eventra init
|
|
41
|
+
eventra sync
|
|
42
|
+
eventra send
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# Commands
|
|
48
|
+
|
|
49
|
+
## eventra init
|
|
50
|
+
|
|
51
|
+
Creates `eventra.json` config file.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
eventra init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"apiKey": "",
|
|
62
|
+
"events": [],
|
|
63
|
+
"wrappers": [],
|
|
64
|
+
"sync": {
|
|
65
|
+
"include": ["**/*.{ts,tsx,js,jsx}"],
|
|
66
|
+
"exclude": ["node_modules", "dist", ".next", ".git"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## eventra sync
|
|
74
|
+
|
|
75
|
+
Scans your project and finds all tracking events automatically.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
eventra sync
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Eventra CLI searches for:
|
|
82
|
+
|
|
83
|
+
### Direct tracking calls
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
tracker.track("feature_created")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Wrapper components
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<TrackedButton event="feature_created" />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Custom wrappers
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<MyComponent event="user_signup" />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
# Wrapper detection
|
|
104
|
+
|
|
105
|
+
If you use wrapper components, Eventra CLI will ask:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Use wrapper components? (Y/n)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Then:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Wrapper component name:
|
|
115
|
+
> TrackedButton
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Event prop name:
|
|
120
|
+
> event
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
You can add multiple wrappers:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Add another wrapper? (y/N)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Example config:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"wrappers": [
|
|
134
|
+
{
|
|
135
|
+
"name": "TrackedButton",
|
|
136
|
+
"prop": "event"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "Feature",
|
|
140
|
+
"prop": "name"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
# Example sync output
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
eventra sync
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Output:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Scanning project...
|
|
158
|
+
|
|
159
|
+
Found events:
|
|
160
|
+
|
|
161
|
+
- landing_login_clicked
|
|
162
|
+
- feature_created
|
|
163
|
+
- user_signup
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# Diff detection
|
|
169
|
+
|
|
170
|
+
Eventra CLI automatically detects changes:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
Changes:
|
|
174
|
+
|
|
175
|
+
New events:
|
|
176
|
+
+ landing_signup_clicked
|
|
177
|
+
|
|
178
|
+
Removed events:
|
|
179
|
+
- old_event
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## eventra send
|
|
185
|
+
|
|
186
|
+
Send events to Eventra backend.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
eventra send
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
If API key is missing:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
API key is not configured
|
|
196
|
+
Enter your API key:
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Events are registered in Eventra.
|
|
200
|
+
|
|
201
|
+
Example output:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Events registered successfully
|
|
205
|
+
|
|
206
|
+
New events:
|
|
207
|
+
+ feature_created
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
# Example workflow
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
eventra init
|
|
216
|
+
eventra sync
|
|
217
|
+
eventra send
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
# Configuration
|
|
223
|
+
|
|
224
|
+
eventra.json
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"apiKey": "",
|
|
229
|
+
"events": [],
|
|
230
|
+
"wrappers": [],
|
|
231
|
+
"sync": {
|
|
232
|
+
"include": ["**/*.{ts,tsx,js,jsx}"],
|
|
233
|
+
"exclude": ["node_modules", "dist", ".next", ".git"]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
# Supported patterns
|
|
241
|
+
|
|
242
|
+
## Direct tracking
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
tracker.track("feature_created")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Wrapper components
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<TrackedButton event="feature_created" />
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Custom wrapper props
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
<MyComponent eventName="user_signup" />
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
# Supported frameworks
|
|
263
|
+
|
|
264
|
+
Eventra CLI works with:
|
|
265
|
+
|
|
266
|
+
- React
|
|
267
|
+
- Next.js
|
|
268
|
+
- Vue
|
|
269
|
+
- Nuxt
|
|
270
|
+
- Svelte
|
|
271
|
+
- Astro
|
|
272
|
+
- Node.js
|
|
273
|
+
- Express
|
|
274
|
+
- NestJS
|
|
275
|
+
- Vanilla JavaScript
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
# Important
|
|
280
|
+
|
|
281
|
+
Eventra CLI detects only string literals:
|
|
282
|
+
|
|
283
|
+
Supported:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
track("event_name")
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Not supported:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
track(eventName)
|
|
293
|
+
track(EVENTS.event)
|
|
294
|
+
track(getEvent())
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
This ensures reliable and predictable event detection.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# Requirements
|
|
302
|
+
|
|
303
|
+
- Node.js 18+
|
|
304
|
+
- JavaScript / TypeScript project
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
# License
|
|
309
|
+
|
|
310
|
+
MIT
|
|
@@ -0,0 +1,42 @@
|
|
|
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.init = init;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const config_1 = require("../utils/config");
|
|
12
|
+
async function init() {
|
|
13
|
+
console.log(chalk_1.default.blue("Initializing Eventra..."));
|
|
14
|
+
const configPath = path_1.default.join(process.cwd(), config_1.CONFIG_NAME);
|
|
15
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
16
|
+
console.log(chalk_1.default.yellow("eventra.json already exists"));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const answers = await inquirer_1.default.prompt([
|
|
20
|
+
{
|
|
21
|
+
type: "input",
|
|
22
|
+
name: "apiKey",
|
|
23
|
+
message: "API Key (optional):"
|
|
24
|
+
}
|
|
25
|
+
]);
|
|
26
|
+
const config = {
|
|
27
|
+
apiKey: answers.apiKey || "",
|
|
28
|
+
events: [],
|
|
29
|
+
wrappers: [],
|
|
30
|
+
sync: {
|
|
31
|
+
include: ["**/*.{ts,tsx,js,jsx}"],
|
|
32
|
+
exclude: [
|
|
33
|
+
"node_modules",
|
|
34
|
+
"dist",
|
|
35
|
+
".next",
|
|
36
|
+
".git"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
await (0, config_1.saveConfig)(config);
|
|
41
|
+
console.log(chalk_1.default.green("eventra.json created"));
|
|
42
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
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.send = send;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const config_1 = require("../utils/config");
|
|
10
|
+
const EVENTRA_ENDPOINT = process.env.EVENTRA_ENDPOINT ?? "";
|
|
11
|
+
const CLI_VERSION = "0.0.1";
|
|
12
|
+
async function send() {
|
|
13
|
+
let config = await (0, config_1.loadConfig)();
|
|
14
|
+
if (!config) {
|
|
15
|
+
console.log(chalk_1.default.red("eventra.json not found. Run 'eventra init'"));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let apiKey = config.apiKey;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
const answers = await inquirer_1.default.prompt([
|
|
21
|
+
{
|
|
22
|
+
type: "input",
|
|
23
|
+
name: "apiKey",
|
|
24
|
+
message: "Enter your API key:"
|
|
25
|
+
}
|
|
26
|
+
]);
|
|
27
|
+
apiKey = answers.apiKey;
|
|
28
|
+
config.apiKey = apiKey;
|
|
29
|
+
await (0, config_1.saveConfig)(config);
|
|
30
|
+
console.log(chalk_1.default.green("API key saved"));
|
|
31
|
+
}
|
|
32
|
+
if (!config.events.length) {
|
|
33
|
+
console.log(chalk_1.default.yellow("No events found. Run 'eventra sync'"));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk_1.default.blue("Sending events..."));
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(EVENTRA_ENDPOINT, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
"x-api-key": apiKey
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
events: config.events,
|
|
46
|
+
cli: {
|
|
47
|
+
name: "@eventra_dev/eventra-cli",
|
|
48
|
+
version: CLI_VERSION,
|
|
49
|
+
runtime: "node"
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
if (res.status >= 400) {
|
|
54
|
+
console.log(chalk_1.default.red(`Failed (${res.status})`));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const data = await res.json();
|
|
58
|
+
console.log(chalk_1.default.green("Events registered successfully"));
|
|
59
|
+
if (data.created?.length) {
|
|
60
|
+
console.log(chalk_1.default.green("\nNew events:"));
|
|
61
|
+
data.created.forEach((e) => console.log(chalk_1.default.green(`+ ${e}`)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
console.log(chalk_1.default.red("Network error"));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
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.sync = sync;
|
|
7
|
+
const ts_morph_1 = require("ts-morph");
|
|
8
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const config_1 = require("../utils/config");
|
|
12
|
+
async function sync() {
|
|
13
|
+
let config = await (0, config_1.loadConfig)();
|
|
14
|
+
if (!config) {
|
|
15
|
+
console.log(chalk_1.default.red("Run 'eventra init' first"));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const events = new Set();
|
|
19
|
+
const project = new ts_morph_1.Project();
|
|
20
|
+
console.log(chalk_1.default.blue("Scanning project..."));
|
|
21
|
+
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
22
|
+
ignore: config.sync.exclude
|
|
23
|
+
});
|
|
24
|
+
// track()
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
const sourceFile = project.addSourceFileAtPath(file);
|
|
27
|
+
const calls = sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
|
|
28
|
+
for (const call of calls) {
|
|
29
|
+
const expression = call.getExpression();
|
|
30
|
+
if (expression.getKind() ===
|
|
31
|
+
ts_morph_1.SyntaxKind.PropertyAccessExpression) {
|
|
32
|
+
const prop = expression.asKind(ts_morph_1.SyntaxKind.PropertyAccessExpression);
|
|
33
|
+
if (!prop)
|
|
34
|
+
continue;
|
|
35
|
+
if (prop.getName() !== "track")
|
|
36
|
+
continue;
|
|
37
|
+
const args = call.getArguments();
|
|
38
|
+
const eventArg = args[0];
|
|
39
|
+
if (!eventArg)
|
|
40
|
+
continue;
|
|
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
|
+
}
|
|
107
|
+
}
|
|
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
|
+
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
|
+
config.events = list;
|
|
214
|
+
await (0, config_1.saveConfig)(config);
|
|
215
|
+
console.log(chalk_1.default.green("eventra.json updated"));
|
|
216
|
+
}
|