@eventra_dev/eventra-cli 0.0.4 → 0.0.5

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.
@@ -1,31 +1,39 @@
1
- import { Project, SyntaxKind } from "ts-morph";
2
- import fg from "fast-glob";
3
1
  import chalk from "chalk";
4
- import inquirer from "inquirer";
2
+ import fg from "fast-glob";
3
+ import { Project } from "ts-morph";
4
+ import fs from "fs/promises";
5
+
5
6
  import {
6
7
  loadConfig,
7
8
  saveConfig
8
9
  } from "../utils/config";
9
10
 
11
+ import { detectParser } from "../utils/parsers/router";
12
+ import { parseVue } from "../utils/parsers/vue";
13
+ import { parseSvelte } from "../utils/parsers/svelte";
14
+ import { parseAstro } from "../utils/parsers/astro";
15
+
16
+ import { scanTrack } from "../utils/scanners/track";
17
+ import { scanFunctionWrappers } from "../utils/scanners/function-wrappers";
18
+ import { scanComponentWrappers } from "../utils/scanners/component-wrappers";
19
+
10
20
  export async function sync() {
11
- let config = await loadConfig();
21
+ const config = await loadConfig();
12
22
 
13
23
  if (!config) {
14
24
  console.log(
15
- chalk.red(
16
- "Run 'eventra init' first"
17
- )
25
+ chalk.red("Run 'eventra init'")
18
26
  );
19
27
  return;
20
28
  }
21
29
 
22
- const events = new Set<string>();
23
- const project = new Project();
24
-
25
30
  console.log(
26
31
  chalk.blue("Scanning project...")
27
32
  );
28
33
 
34
+ const project = new Project();
35
+ const events = new Set<string>();
36
+
29
37
  const files = await fg(
30
38
  config.sync.include,
31
39
  {
@@ -33,357 +41,53 @@ export async function sync() {
33
41
  }
34
42
  );
35
43
 
36
- // track()
37
44
  for (const file of files) {
38
- const sourceFile =
39
- project.addSourceFileAtPath(file);
45
+ const parser =
46
+ detectParser(file);
40
47
 
41
- const calls =
42
- sourceFile.getDescendantsOfKind(
43
- SyntaxKind.CallExpression
48
+ let content =
49
+ await fs.readFile(
50
+ file,
51
+ "utf-8"
44
52
  );
45
53
 
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();
54
+ if (parser === "vue")
55
+ content = parseVue(content);
208
56
 
209
- for (const attr of attrs) {
210
- if (
211
- attr.getKind() !==
212
- SyntaxKind.JsxAttribute
213
- )
214
- continue;
57
+ if (parser === "svelte")
58
+ content = parseSvelte(content);
215
59
 
216
- const attrNode =
217
- attr.asKind(
218
- SyntaxKind.JsxAttribute
219
- );
60
+ if (parser === "astro")
61
+ content = parseAstro(content);
220
62
 
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
- );
63
+ const source =
64
+ project.createSourceFile(
65
+ file,
66
+ content,
67
+ { overwrite: true }
68
+ );
314
69
 
315
- const removed =
316
- previous.filter(
317
- (e: string) =>
318
- !list.includes(e)
70
+ scanTrack(source).forEach(
71
+ (e) => events.add(e)
319
72
  );
320
73
 
321
- if (added.length || removed.length) {
322
- console.log(
323
- chalk.blue("Changes:")
74
+ scanFunctionWrappers(
75
+ source,
76
+ config.functionWrappers ?? []
77
+ ).forEach((e) =>
78
+ events.add(e)
324
79
  );
325
80
 
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
- )
81
+ scanComponentWrappers(
82
+ source,
83
+ config.wrappers ?? []
84
+ ).forEach((e) =>
85
+ events.add(e)
364
86
  );
365
87
  }
366
88
 
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
- }
89
+ const list =
90
+ [...events].sort();
387
91
 
388
92
  config.events = list;
389
93
 
@@ -391,7 +95,7 @@ export async function sync() {
391
95
 
392
96
  console.log(
393
97
  chalk.green(
394
- "eventra.json updated"
98
+ `Found ${list.length} events`
395
99
  )
396
100
  );
397
101
  }
package/src/types.ts ADDED
@@ -0,0 +1,20 @@
1
+ export type ComponentWrapper = {
2
+ name: string;
3
+ prop: string;
4
+ };
5
+
6
+ export type FunctionWrapper = {
7
+ name: string;
8
+ path: string;
9
+ };
10
+
11
+ export type EventraConfig = {
12
+ apiKey?: string;
13
+ events: string[];
14
+ wrappers: ComponentWrapper[];
15
+ functionWrappers: FunctionWrapper[];
16
+ sync: {
17
+ include: string[];
18
+ exclude: string[];
19
+ };
20
+ };
@@ -1,15 +1,22 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "path";
3
+ import { EventraConfig } from "../types";
3
4
 
4
5
  export const CONFIG_NAME = "eventra.json";
5
6
 
6
- export function normalizeConfig(config: any) {
7
+ export function normalizeConfig(
8
+ config: Partial<EventraConfig>
9
+ ): EventraConfig {
7
10
  return {
8
11
  apiKey: config.apiKey ?? "",
9
12
  events: config.events ?? [],
10
13
  wrappers: config.wrappers ?? [],
14
+ functionWrappers:
15
+ config.functionWrappers ?? [],
11
16
  sync: config.sync ?? {
12
- include: ["**/*.{ts,tsx,js,jsx}"],
17
+ include: [
18
+ "**/*.{ts,tsx,js,jsx}"
19
+ ],
13
20
  exclude: [
14
21
  "node_modules",
15
22
  "dist",
@@ -20,28 +27,38 @@ export function normalizeConfig(config: any) {
20
27
  };
21
28
  }
22
29
 
23
- export async function loadConfig() {
30
+ export async function loadConfig(): Promise<EventraConfig | null> {
24
31
  const configPath = path.join(
25
32
  process.cwd(),
26
33
  CONFIG_NAME
27
34
  );
28
35
 
29
- if (!(await fs.pathExists(configPath))) {
36
+ if (
37
+ !(await fs.pathExists(
38
+ configPath
39
+ ))
40
+ ) {
30
41
  return null;
31
42
  }
32
43
 
33
- const config = await fs.readJSON(configPath);
44
+ const config =
45
+ await fs.readJSON(
46
+ configPath
47
+ );
34
48
 
35
49
  return normalizeConfig(config);
36
50
  }
37
51
 
38
- export async function saveConfig(config: any) {
52
+ export async function saveConfig(
53
+ config: EventraConfig
54
+ ) {
39
55
  const configPath = path.join(
40
56
  process.cwd(),
41
57
  CONFIG_NAME
42
58
  );
43
59
 
44
- const normalized = normalizeConfig(config);
60
+ const normalized =
61
+ normalizeConfig(config);
45
62
 
46
63
  await fs.writeJSON(
47
64
  configPath,
@@ -0,0 +1,51 @@
1
+ import {
2
+ CallExpression,
3
+ Node,
4
+ ObjectLiteralExpression,
5
+ } from "ts-morph";
6
+
7
+ export function extractEvent(
8
+ call: CallExpression,
9
+ path: string
10
+ ): string | null {
11
+ const parts = path.split(".");
12
+
13
+ let node: Node | undefined =
14
+ call.getArguments()[Number(parts[0])];
15
+
16
+ if (!node) return null;
17
+
18
+ for (let i = 1; i < parts.length; i++) {
19
+ if (
20
+ Node.isObjectLiteralExpression(node)
21
+ ) {
22
+ const obj: ObjectLiteralExpression =
23
+ node;
24
+
25
+ const prop =
26
+ obj.getProperty(parts[i]);
27
+
28
+ if (!prop) return null;
29
+
30
+ if (
31
+ Node.isPropertyAssignment(prop)
32
+ ) {
33
+ const initializer =
34
+ prop.getInitializer();
35
+
36
+ if (!initializer) return null;
37
+
38
+ node = initializer;
39
+ }
40
+ }
41
+ }
42
+
43
+ if (
44
+ node &&
45
+ Node.isStringLiteral(node)
46
+ ) {
47
+ return node.getLiteralText();
48
+ }
49
+
50
+ return null;
51
+ }
@@ -0,0 +1,5 @@
1
+ export function parseAstro(
2
+ content: string
3
+ ) {
4
+ return content;
5
+ }
@@ -0,0 +1,14 @@
1
+ export function detectParser(
2
+ file: string
3
+ ) {
4
+ if (file.endsWith(".vue"))
5
+ return "vue";
6
+
7
+ if (file.endsWith(".svelte"))
8
+ return "svelte";
9
+
10
+ if (file.endsWith(".astro"))
11
+ return "astro";
12
+
13
+ return "ts";
14
+ }
@@ -0,0 +1,5 @@
1
+ export function parseSvelte(
2
+ content: string
3
+ ) {
4
+ return content;
5
+ }
@@ -0,0 +1,19 @@
1
+ export function parseVue(
2
+ content: string
3
+ ) {
4
+ const template =
5
+ content.match(
6
+ /<template[\s\S]*?>([\s\S]*?)<\/template>/
7
+ );
8
+
9
+ const script =
10
+ content.match(
11
+ /<script[\s\S]*?>([\s\S]*?)<\/script>/
12
+ );
13
+
14
+ return (
15
+ (template?.[1] ?? "") +
16
+ "\n" +
17
+ (script?.[1] ?? "")
18
+ );
19
+ }