@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/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
38
+ const program = new commander_1.Command();
39
+ program
40
+ .name("eventra")
41
+ .description("Eventra CLI")
42
+ .version("0.0.1");
43
+ program
44
+ .command("init")
45
+ .description("Initialize Eventra")
46
+ .action(async () => {
47
+ const { init } = await Promise.resolve().then(() => __importStar(require("./commands/init")));
48
+ await init();
49
+ });
50
+ program
51
+ .command("sync")
52
+ .description("Sync events")
53
+ .action(async () => {
54
+ const { sync } = await Promise.resolve().then(() => __importStar(require("./commands/sync")));
55
+ await sync();
56
+ });
57
+ program
58
+ .command("send")
59
+ .description("Send events")
60
+ .action(async () => {
61
+ const { send } = await Promise.resolve().then(() => __importStar(require("./commands/send")));
62
+ await send();
63
+ });
64
+ program.parse();
@@ -0,0 +1,43 @@
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.CONFIG_NAME = void 0;
7
+ exports.normalizeConfig = normalizeConfig;
8
+ exports.loadConfig = loadConfig;
9
+ exports.saveConfig = saveConfig;
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const path_1 = __importDefault(require("path"));
12
+ exports.CONFIG_NAME = "eventra.json";
13
+ function normalizeConfig(config) {
14
+ return {
15
+ apiKey: config.apiKey ?? "",
16
+ events: config.events ?? [],
17
+ wrappers: config.wrappers ?? [],
18
+ sync: config.sync ?? {
19
+ include: ["**/*.{ts,tsx,js,jsx}"],
20
+ exclude: [
21
+ "node_modules",
22
+ "dist",
23
+ ".next",
24
+ ".git"
25
+ ]
26
+ }
27
+ };
28
+ }
29
+ async function loadConfig() {
30
+ const configPath = path_1.default.join(process.cwd(), exports.CONFIG_NAME);
31
+ if (!(await fs_extra_1.default.pathExists(configPath))) {
32
+ return null;
33
+ }
34
+ const config = await fs_extra_1.default.readJSON(configPath);
35
+ return normalizeConfig(config);
36
+ }
37
+ async function saveConfig(config) {
38
+ const configPath = path_1.default.join(process.cwd(), exports.CONFIG_NAME);
39
+ const normalized = normalizeConfig(config);
40
+ await fs_extra_1.default.writeJSON(configPath, normalized, {
41
+ spaces: 2
42
+ });
43
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@eventra_dev/eventra-cli",
3
+ "version": "0.0.1",
4
+ "description": "Eventra CLI",
5
+ "type": "commonjs",
6
+ "bin": {
7
+ "eventra": "./dist/index.js"
8
+ },
9
+ "access": "public",
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "build": "tsc && chmod +x dist/index.js",
13
+ "dev": "tsx src/index.ts"
14
+ },
15
+ "dependencies": {
16
+ "chalk": "^4.1.2",
17
+ "commander": "^11.0.0",
18
+ "fast-glob": "^3.3.3",
19
+ "fs-extra": "^11.2.0",
20
+ "inquirer": "^8.2.7",
21
+ "ts-morph": "^27.0.2"
22
+ },
23
+ "devDependencies": {
24
+ "@types/fs-extra": "^11.0.4",
25
+ "@types/inquirer": "^9.0.9",
26
+ "@types/node": "^20.0.0",
27
+ "tsx": "^4.7.0",
28
+ "typescript": "^5.3.0"
29
+ }
30
+ }
@@ -0,0 +1,60 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import inquirer from "inquirer";
5
+ import {
6
+ CONFIG_NAME,
7
+ saveConfig
8
+ } from "../utils/config";
9
+
10
+ export async function init() {
11
+ console.log(
12
+ chalk.blue("Initializing Eventra...")
13
+ );
14
+
15
+ const configPath = path.join(
16
+ process.cwd(),
17
+ CONFIG_NAME
18
+ );
19
+
20
+ if (await fs.pathExists(configPath)) {
21
+ console.log(
22
+ chalk.yellow(
23
+ "eventra.json already exists"
24
+ )
25
+ );
26
+ return;
27
+ }
28
+
29
+ const answers = await inquirer.prompt([
30
+ {
31
+ type: "input",
32
+ name: "apiKey",
33
+ message:
34
+ "API Key (optional):"
35
+ }
36
+ ]);
37
+
38
+ const config = {
39
+ apiKey: answers.apiKey || "",
40
+ events: [],
41
+ wrappers: [],
42
+ sync: {
43
+ include: ["**/*.{ts,tsx,js,jsx}"],
44
+ exclude: [
45
+ "node_modules",
46
+ "dist",
47
+ ".next",
48
+ ".git"
49
+ ]
50
+ }
51
+ };
52
+
53
+ await saveConfig(config);
54
+
55
+ console.log(
56
+ chalk.green(
57
+ "eventra.json created"
58
+ )
59
+ );
60
+ }
@@ -0,0 +1,124 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import {
4
+ loadConfig,
5
+ saveConfig
6
+ } from "../utils/config";
7
+
8
+ const EVENTRA_ENDPOINT = process.env.EVENTRA_ENDPOINT ?? "";
9
+
10
+ const CLI_VERSION = "0.0.1";
11
+
12
+ export async function send() {
13
+ let config = await loadConfig();
14
+
15
+ if (!config) {
16
+ console.log(
17
+ chalk.red(
18
+ "eventra.json not found. Run 'eventra init'"
19
+ )
20
+ );
21
+ return;
22
+ }
23
+
24
+ let apiKey = config.apiKey;
25
+
26
+ if (!apiKey) {
27
+ const answers =
28
+ await inquirer.prompt([
29
+ {
30
+ type: "input",
31
+ name: "apiKey",
32
+ message:
33
+ "Enter your API key:"
34
+ }
35
+ ]);
36
+
37
+ apiKey = answers.apiKey;
38
+
39
+ config.apiKey = apiKey;
40
+
41
+ await saveConfig(config);
42
+
43
+ console.log(
44
+ chalk.green(
45
+ "API key saved"
46
+ )
47
+ );
48
+ }
49
+
50
+ if (!config.events.length) {
51
+ console.log(
52
+ chalk.yellow(
53
+ "No events found. Run 'eventra sync'"
54
+ )
55
+ );
56
+ return;
57
+ }
58
+
59
+ console.log(
60
+ chalk.blue("Sending events...")
61
+ );
62
+
63
+ try {
64
+ const res = await fetch(EVENTRA_ENDPOINT,
65
+ {
66
+ method: "POST",
67
+ headers: {
68
+ "Content-Type":
69
+ "application/json",
70
+ "x-api-key": apiKey
71
+ },
72
+ body: JSON.stringify({
73
+ events: config.events,
74
+ cli: {
75
+ name:
76
+ "@eventra_dev/eventra-cli",
77
+ version: CLI_VERSION,
78
+ runtime: "node"
79
+ }
80
+ })
81
+ }
82
+ );
83
+
84
+ if (res.status >= 400) {
85
+ console.log(
86
+ chalk.red(
87
+ `Failed (${res.status})`
88
+ )
89
+ );
90
+
91
+ return;
92
+ }
93
+
94
+ const data = await res.json();
95
+
96
+ console.log(
97
+ chalk.green(
98
+ "Events registered successfully"
99
+ )
100
+ );
101
+
102
+ if (data.created?.length) {
103
+ console.log(
104
+ chalk.green(
105
+ "\nNew events:"
106
+ )
107
+ );
108
+
109
+ data.created.forEach(
110
+ (e: string) =>
111
+ console.log(
112
+ chalk.green(
113
+ `+ ${e}`
114
+ )
115
+ )
116
+ );
117
+ }
118
+
119
+ } catch {
120
+ console.log(
121
+ chalk.red("Network error")
122
+ );
123
+ }
124
+ }
@@ -0,0 +1,397 @@
1
+ import { Project, SyntaxKind } from "ts-morph";
2
+ import fg from "fast-glob";
3
+ import chalk from "chalk";
4
+ import inquirer from "inquirer";
5
+ import {
6
+ loadConfig,
7
+ saveConfig
8
+ } from "../utils/config";
9
+
10
+ export async function sync() {
11
+ let config = await loadConfig();
12
+
13
+ if (!config) {
14
+ console.log(
15
+ chalk.red(
16
+ "Run 'eventra init' first"
17
+ )
18
+ );
19
+ return;
20
+ }
21
+
22
+ const events = new Set<string>();
23
+ const project = new Project();
24
+
25
+ console.log(
26
+ chalk.blue("Scanning project...")
27
+ );
28
+
29
+ const files = await fg(
30
+ config.sync.include,
31
+ {
32
+ ignore: config.sync.exclude
33
+ }
34
+ );
35
+
36
+ // track()
37
+ for (const file of files) {
38
+ const sourceFile =
39
+ project.addSourceFileAtPath(file);
40
+
41
+ const calls =
42
+ sourceFile.getDescendantsOfKind(
43
+ SyntaxKind.CallExpression
44
+ );
45
+
46
+ for (const call of calls) {
47
+ const expression =
48
+ call.getExpression();
49
+
50
+ if (
51
+ expression.getKind() ===
52
+ SyntaxKind.PropertyAccessExpression
53
+ ) {
54
+ const prop =
55
+ expression.asKind(
56
+ SyntaxKind.PropertyAccessExpression
57
+ );
58
+
59
+ if (!prop) continue;
60
+
61
+ if (prop.getName() !== "track")
62
+ continue;
63
+
64
+ const args =
65
+ call.getArguments();
66
+
67
+ const eventArg =
68
+ args[0];
69
+
70
+ if (!eventArg) continue;
71
+
72
+ let event: string | null = null;
73
+
74
+ // "event"
75
+ if (
76
+ eventArg.getKind() ===
77
+ SyntaxKind.StringLiteral
78
+ ) {
79
+ event =
80
+ eventArg
81
+ .asKindOrThrow(
82
+ SyntaxKind.StringLiteral
83
+ )
84
+ .getLiteralText();
85
+ }
86
+
87
+ // `event`
88
+ if (
89
+ eventArg.getKind() ===
90
+ SyntaxKind.NoSubstitutionTemplateLiteral
91
+ ) {
92
+ event =
93
+ eventArg
94
+ .asKindOrThrow(
95
+ SyntaxKind.NoSubstitutionTemplateLiteral
96
+ )
97
+ .getLiteralText();
98
+ }
99
+
100
+ if (event) {
101
+ events.add(event);
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ console.log(
108
+ chalk.green(
109
+ `Found ${events.size} track events`
110
+ )
111
+ );
112
+
113
+ // wrappers setup
114
+ if (!config.wrappers.length) {
115
+ const { useWrapper } =
116
+ await inquirer.prompt([
117
+ {
118
+ type: "confirm",
119
+ name: "useWrapper",
120
+ message:
121
+ "Use wrapper components?",
122
+ default: true
123
+ }
124
+ ]);
125
+
126
+ if (useWrapper) {
127
+ const wrappers = [];
128
+
129
+ let addMore = true;
130
+
131
+ while (addMore) {
132
+ const answers =
133
+ await inquirer.prompt([
134
+ {
135
+ type: "input",
136
+ name: "name",
137
+ message:
138
+ "Wrapper component name:"
139
+ },
140
+ {
141
+ type: "input",
142
+ name: "prop",
143
+ message:
144
+ "Event prop name:"
145
+ }
146
+ ]);
147
+
148
+ wrappers.push({
149
+ name: answers.name,
150
+ prop: answers.prop
151
+ });
152
+
153
+ const more =
154
+ await inquirer.prompt([
155
+ {
156
+ type: "confirm",
157
+ name: "more",
158
+ message:
159
+ "Add another wrapper?",
160
+ default: false
161
+ }
162
+ ]);
163
+
164
+ addMore = more.more;
165
+ }
166
+
167
+ config.wrappers = wrappers;
168
+ }
169
+ }
170
+
171
+ // scan wrappers
172
+ if (config.wrappers.length) {
173
+ console.log(
174
+ chalk.blue(
175
+ "Scanning wrappers..."
176
+ )
177
+ );
178
+
179
+ for (const file of files) {
180
+ const sourceFile =
181
+ project.addSourceFileAtPath(file);
182
+
183
+ const elements = [
184
+ ...sourceFile.getDescendantsOfKind(
185
+ SyntaxKind.JsxOpeningElement
186
+ ),
187
+ ...sourceFile.getDescendantsOfKind(
188
+ SyntaxKind.JsxSelfClosingElement
189
+ )
190
+ ];
191
+
192
+ for (const element of elements) {
193
+ const tagName =
194
+ element
195
+ .getTagNameNode()
196
+ .getText()
197
+ .toLowerCase();
198
+
199
+ for (const wrapper of config.wrappers) {
200
+ if (
201
+ tagName !==
202
+ wrapper.name.toLowerCase()
203
+ )
204
+ continue;
205
+
206
+ const attrs =
207
+ element.getAttributes();
208
+
209
+ for (const attr of attrs) {
210
+ if (
211
+ attr.getKind() !==
212
+ SyntaxKind.JsxAttribute
213
+ )
214
+ continue;
215
+
216
+ const attrNode =
217
+ attr.asKind(
218
+ SyntaxKind.JsxAttribute
219
+ );
220
+
221
+ if (!attrNode) continue;
222
+
223
+ const attrName =
224
+ attrNode
225
+ .getNameNode()
226
+ .getText()
227
+ .toLowerCase();
228
+
229
+ if (
230
+ attrName !==
231
+ wrapper.prop.toLowerCase()
232
+ )
233
+ continue;
234
+
235
+ const initializer =
236
+ attrNode.getInitializer();
237
+
238
+ if (!initializer) continue;
239
+
240
+ let value: string | null = null;
241
+
242
+ // event="signup"
243
+ if (
244
+ initializer.getKind() ===
245
+ SyntaxKind.StringLiteral
246
+ ) {
247
+ value =
248
+ initializer
249
+ .asKindOrThrow(
250
+ SyntaxKind.StringLiteral
251
+ )
252
+ .getLiteralText();
253
+ }
254
+
255
+ // event={"signup"}
256
+ if (
257
+ initializer.getKind() ===
258
+ SyntaxKind.JsxExpression
259
+ ) {
260
+ const expr =
261
+ initializer
262
+ .asKindOrThrow(
263
+ SyntaxKind.JsxExpression
264
+ )
265
+ .getExpression();
266
+
267
+ if (
268
+ expr?.getKind() ===
269
+ SyntaxKind.StringLiteral
270
+ ) {
271
+ value =
272
+ expr
273
+ .asKindOrThrow(
274
+ SyntaxKind.StringLiteral
275
+ )
276
+ .getLiteralText();
277
+ }
278
+ }
279
+
280
+ if (value) {
281
+ events.add(value);
282
+ }
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ // results
290
+ const list =
291
+ [...events].sort();
292
+
293
+ console.log("");
294
+
295
+ console.log(
296
+ chalk.green("Found events:")
297
+ );
298
+
299
+ list.forEach((e) =>
300
+ console.log(
301
+ chalk.gray(`- ${e}`)
302
+ )
303
+ );
304
+
305
+ console.log("");
306
+
307
+ // diff
308
+ const previous =
309
+ config.events ?? [];
310
+
311
+ const added = list.filter(
312
+ (e) => !previous.includes(e)
313
+ );
314
+
315
+ const removed =
316
+ previous.filter(
317
+ (e: string) =>
318
+ !list.includes(e)
319
+ );
320
+
321
+ if (added.length || removed.length) {
322
+ console.log(
323
+ chalk.blue("Changes:")
324
+ );
325
+
326
+ if (added.length) {
327
+ console.log(
328
+ chalk.green(
329
+ "New events:"
330
+ )
331
+ );
332
+
333
+ added.forEach((e) =>
334
+ console.log(
335
+ chalk.green(
336
+ `+ ${e}`
337
+ )
338
+ )
339
+ );
340
+ }
341
+
342
+ if (removed.length) {
343
+ console.log(
344
+ chalk.red(
345
+ "Removed events:"
346
+ )
347
+ );
348
+
349
+ removed.forEach((e: unknown) =>
350
+ console.log(
351
+ chalk.red(
352
+ `- ${e}`
353
+ )
354
+ )
355
+ );
356
+ }
357
+
358
+ console.log("");
359
+ } else {
360
+ console.log(
361
+ chalk.gray(
362
+ "No changes detected"
363
+ )
364
+ );
365
+ }
366
+
367
+ // confirm
368
+ const { confirm } =
369
+ await inquirer.prompt([
370
+ {
371
+ type: "confirm",
372
+ name: "confirm",
373
+ message:
374
+ "Sync these events?",
375
+ default: true
376
+ }
377
+ ]);
378
+
379
+ if (!confirm) {
380
+ console.log(
381
+ chalk.yellow(
382
+ "Sync cancelled"
383
+ )
384
+ );
385
+ return;
386
+ }
387
+
388
+ config.events = list;
389
+
390
+ await saveConfig(config);
391
+
392
+ console.log(
393
+ chalk.green(
394
+ "eventra.json updated"
395
+ )
396
+ );
397
+ }