@eventra_dev/eventra-cli 0.1.1 → 0.1.3
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/README.md +48 -73
- package/dist/commands/check.js +18 -7
- package/dist/commands/send.js +7 -1
- package/dist/commands/sync.js +5 -3
- package/dist/commands/watch.js +13 -55
- package/dist/engine/callResolver.js +15 -24
- package/dist/engine/engine.js +32 -7
- package/dist/engine/graph.js +50 -9
- package/dist/engine/hash.js +10 -0
- package/dist/engine/languageService.js +74 -0
- package/dist/engine/resolver.js +88 -24
- package/dist/engine/scanner.js +57 -67
- package/dist/utils/config.js +7 -2
- package/dist/utils/parsers/astro.js +8 -1
- package/dist/utils/parsers/router.js +1 -0
- package/dist/utils/parsers/svelte.js +4 -1
- package/dist/utils/parsers/vue.js +13 -4
- package/dist/utils/processFile.js +3 -1
- package/package.json +1 -1
- package/src/commands/check.ts +21 -7
- package/src/commands/send.ts +8 -2
- package/src/commands/sync.ts +6 -3
- package/src/commands/watch.ts +14 -60
- package/src/engine/callResolver.ts +21 -32
- package/src/engine/engine.ts +40 -7
- package/src/engine/graph.ts +58 -11
- package/src/engine/hash.ts +5 -0
- package/src/engine/languageService.ts +103 -0
- package/src/engine/resolver.ts +104 -24
- package/src/engine/scanner.ts +67 -74
- package/src/types.ts +5 -0
- package/src/utils/config.ts +15 -33
- package/src/utils/parsers/astro.ts +11 -4
- package/src/utils/parsers/router.ts +5 -10
- package/src/utils/parsers/svelte.ts +6 -4
- package/src/utils/parsers/vue.ts +15 -5
- package/src/utils/processFile.ts +6 -3
- package/dist/engine/extract.js +0 -53
- package/dist/engine/flow.js +0 -38
- package/dist/engine/project.js +0 -74
- package/dist/engine/symbol.js +0 -31
- package/src/engine/extract.ts +0 -74
- package/src/engine/flow.ts +0 -54
- package/src/engine/project.ts +0 -75
- package/src/engine/symbol.ts +0 -42
package/src/engine/engine.ts
CHANGED
|
@@ -1,26 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TSService } from "./languageService";
|
|
2
2
|
import { scanSource } from "./scanner";
|
|
3
|
+
import { DependencyGraph } from "./graph";
|
|
4
|
+
import { hash } from "./hash";
|
|
3
5
|
|
|
4
6
|
export class EventraEngine {
|
|
5
|
-
private
|
|
7
|
+
private ts: TSService;
|
|
8
|
+
private graph = new DependencyGraph();
|
|
9
|
+
|
|
6
10
|
private fileEvents = new Map<string, Set<string>>();
|
|
11
|
+
private fileHash = new Map<string, string>();
|
|
7
12
|
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
13
|
+
constructor(root: string) {
|
|
14
|
+
this.ts = new TSService(root);
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
scanFile(file: string, content: string) {
|
|
13
|
-
const
|
|
14
|
-
|
|
18
|
+
const h = hash(content);
|
|
19
|
+
|
|
20
|
+
if (this.fileHash.get(file) === h) {
|
|
21
|
+
return this.fileEvents.get(file) ?? new Set();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.fileHash.set(file, h);
|
|
25
|
+
this.ts.updateFile(file, content);
|
|
15
26
|
|
|
27
|
+
const source = this.ts.getSourceFile(file);
|
|
28
|
+
const checker = this.ts.getChecker();
|
|
29
|
+
|
|
30
|
+
if (!source || !checker) return new Set();
|
|
31
|
+
|
|
32
|
+
this.graph.update(file, source);
|
|
16
33
|
const events = scanSource(source, checker);
|
|
17
34
|
|
|
18
35
|
this.fileEvents.set(file, events);
|
|
36
|
+
|
|
19
37
|
return events;
|
|
20
38
|
}
|
|
21
39
|
|
|
22
40
|
updateFile(file: string, content: string) {
|
|
23
|
-
|
|
41
|
+
const events = this.scanFile(file, content);
|
|
42
|
+
const dependents = this.graph.getDependents(file);
|
|
43
|
+
|
|
44
|
+
for (const dep of dependents) {
|
|
45
|
+
const source = this.ts.getSourceFile(dep);
|
|
46
|
+
const checker = this.ts.getChecker();
|
|
47
|
+
|
|
48
|
+
if (!source || !checker) continue;
|
|
49
|
+
|
|
50
|
+
const ev = scanSource(source, checker);
|
|
51
|
+
this.fileEvents.set(dep, ev);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return events;
|
|
24
55
|
}
|
|
25
56
|
|
|
26
57
|
getAllEvents() {
|
|
@@ -35,5 +66,7 @@ export class EventraEngine {
|
|
|
35
66
|
|
|
36
67
|
removeFile(file: string) {
|
|
37
68
|
this.fileEvents.delete(file);
|
|
69
|
+
this.fileHash.delete(file);
|
|
70
|
+
this.graph.remove(file);
|
|
38
71
|
}
|
|
39
72
|
}
|
package/src/engine/graph.ts
CHANGED
|
@@ -1,23 +1,70 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
2
4
|
|
|
3
5
|
export class DependencyGraph {
|
|
4
6
|
private reverse = new Map<string, Set<string>>();
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
private normalize(file: string) {
|
|
9
|
+
return path.resolve(file);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private resolveImport(from: string, module: string): string | null {
|
|
13
|
+
if (!module.startsWith(".")) return null;
|
|
14
|
+
|
|
15
|
+
const base = path.resolve(path.dirname(from), module);
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
const tryFiles = [
|
|
18
|
+
base,
|
|
19
|
+
base + ".ts",
|
|
20
|
+
base + ".tsx",
|
|
21
|
+
base + ".js",
|
|
22
|
+
base + ".jsx",
|
|
23
|
+
path.join(base, "index.ts"),
|
|
24
|
+
path.join(base, "index.tsx"),
|
|
25
|
+
path.join(base, "index.js"),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
for (const f of tryFiles) {
|
|
29
|
+
if (fs.existsSync(f)) {
|
|
30
|
+
return this.normalize(f);
|
|
15
31
|
}
|
|
16
|
-
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return this.normalize(base);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
update(file: string, source: ts.SourceFile) {
|
|
38
|
+
const normalizedFile = this.normalize(file);
|
|
39
|
+
|
|
40
|
+
for (const stmt of source.statements) {
|
|
41
|
+
if (!ts.isImportDeclaration(stmt)) continue;
|
|
42
|
+
|
|
43
|
+
const module = stmt.moduleSpecifier
|
|
44
|
+
.getText()
|
|
45
|
+
.replace(/['"]/g, "");
|
|
46
|
+
|
|
47
|
+
const resolved = this.resolveImport(normalizedFile, module);
|
|
48
|
+
|
|
49
|
+
if (!resolved) continue;
|
|
50
|
+
|
|
51
|
+
if (!this.reverse.has(resolved)) {
|
|
52
|
+
this.reverse.set(resolved, new Set());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.reverse.get(resolved)!.add(normalizedFile);
|
|
17
56
|
}
|
|
18
57
|
}
|
|
19
58
|
|
|
20
59
|
getDependents(file: string) {
|
|
21
|
-
return [...(this.reverse.get(file) ?? [])];
|
|
60
|
+
return [...(this.reverse.get(this.normalize(file)) ?? [])];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
remove(file: string) {
|
|
64
|
+
const normalized = this.normalize(file);
|
|
65
|
+
|
|
66
|
+
for (const set of this.reverse.values()) {
|
|
67
|
+
set.delete(normalized);
|
|
68
|
+
}
|
|
22
69
|
}
|
|
23
70
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
|
|
4
|
+
export class TSService {
|
|
5
|
+
private files = new Map<string, { version: number; content: string }>();
|
|
6
|
+
private service: ts.LanguageService;
|
|
7
|
+
|
|
8
|
+
constructor(private root: string) {
|
|
9
|
+
const configPath = ts.findConfigFile(
|
|
10
|
+
root,
|
|
11
|
+
ts.sys.fileExists,
|
|
12
|
+
"tsconfig.json"
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
let compilerOptions: ts.CompilerOptions = {
|
|
16
|
+
allowJs: true,
|
|
17
|
+
jsx: ts.JsxEmit.Preserve,
|
|
18
|
+
moduleResolution: ts.ModuleResolutionKind.Node16,
|
|
19
|
+
target: ts.ScriptTarget.ESNext,
|
|
20
|
+
esModuleInterop: true,
|
|
21
|
+
allowSyntheticDefaultImports: true,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (configPath) {
|
|
25
|
+
const configFile = ts.readConfigFile(
|
|
26
|
+
configPath,
|
|
27
|
+
ts.sys.readFile
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
31
|
+
configFile.config,
|
|
32
|
+
ts.sys,
|
|
33
|
+
root
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
compilerOptions = {
|
|
37
|
+
...compilerOptions,
|
|
38
|
+
...parsed.options,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const host: ts.LanguageServiceHost = {
|
|
43
|
+
getScriptFileNames: () => [
|
|
44
|
+
...this.files.keys(),
|
|
45
|
+
...ts.sys.readDirectory(
|
|
46
|
+
this.root,
|
|
47
|
+
[".ts", ".tsx"],
|
|
48
|
+
["node_modules", "dist", ".git"]
|
|
49
|
+
)
|
|
50
|
+
],
|
|
51
|
+
getScriptVersion: (file) =>
|
|
52
|
+
this.files.get(file)?.version.toString() ?? "0",
|
|
53
|
+
|
|
54
|
+
getScriptSnapshot: (file) => {
|
|
55
|
+
const f = this.files.get(file);
|
|
56
|
+
|
|
57
|
+
if (f) {
|
|
58
|
+
return ts.ScriptSnapshot.fromString(f.content);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (fs.existsSync(file)) {
|
|
62
|
+
return ts.ScriptSnapshot.fromString(
|
|
63
|
+
fs.readFileSync(file, "utf-8")
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return undefined;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
getCurrentDirectory: () => this.root,
|
|
71
|
+
getCompilationSettings: () => compilerOptions,
|
|
72
|
+
getDefaultLibFileName: (opts) =>
|
|
73
|
+
ts.getDefaultLibFilePath(opts),
|
|
74
|
+
|
|
75
|
+
readFile: ts.sys.readFile,
|
|
76
|
+
fileExists: ts.sys.fileExists,
|
|
77
|
+
readDirectory: ts.sys.readDirectory,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
this.service = ts.createLanguageService(host);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updateFile(file: string, content: string) {
|
|
84
|
+
const prev = this.files.get(file);
|
|
85
|
+
|
|
86
|
+
this.files.set(file, {
|
|
87
|
+
content,
|
|
88
|
+
version: prev ? prev.version + 1 : 1,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getProgram() {
|
|
93
|
+
return this.service.getProgram();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getChecker() {
|
|
97
|
+
return this.getProgram()?.getTypeChecker();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getSourceFile(file: string) {
|
|
101
|
+
return this.getProgram()?.getSourceFile(file);
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/engine/resolver.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from "typescript";
|
|
2
2
|
|
|
3
3
|
export type ResolveResult = {
|
|
4
4
|
values: string[];
|
|
@@ -6,45 +6,125 @@ export type ResolveResult = {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export function resolveNodeValue(
|
|
9
|
-
node: Node,
|
|
10
|
-
checker: ts.TypeChecker
|
|
9
|
+
node: ts.Node,
|
|
10
|
+
checker: ts.TypeChecker,
|
|
11
|
+
paramMap?: Map<string, ts.Expression>,
|
|
12
|
+
seen = new Set<ts.Node>()
|
|
11
13
|
): ResolveResult | null {
|
|
12
|
-
if (!Node.isIdentifier(node)) return null;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (seen.has(node)) return null;
|
|
16
|
+
seen.add(node);
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
// param mapping
|
|
19
|
+
if (ts.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
20
|
+
return resolveNodeValue(
|
|
21
|
+
paramMap.get(node.text)!,
|
|
22
|
+
checker,
|
|
23
|
+
paramMap
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (ts.isStringLiteral(node)) {
|
|
28
|
+
return { values: [node.text], dynamic: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (ts.isTemplateExpression(node)) {
|
|
32
|
+
let result = node.head.text;
|
|
33
|
+
|
|
34
|
+
for (const span of node.templateSpans) {
|
|
35
|
+
result += "*";
|
|
36
|
+
result += span.literal.text;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { values: [result], dynamic: true };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (ts.isConditionalExpression(node)) {
|
|
43
|
+
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
44
|
+
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
values: [...new Set([...(a?.values ?? []), ...(b?.values ?? [])])],
|
|
48
|
+
dynamic: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
18
51
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
52
|
+
if (ts.isIdentifier(node)) {
|
|
53
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
54
|
+
if (!symbol) return null;
|
|
22
55
|
|
|
23
|
-
|
|
24
|
-
|
|
56
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
57
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const decl of symbol.getDeclarations() ?? []) {
|
|
61
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
62
|
+
return resolveNodeValue(decl.initializer, checker, paramMap, seen);
|
|
25
63
|
}
|
|
26
64
|
|
|
27
|
-
if (ts.
|
|
28
|
-
|
|
65
|
+
if (ts.isEnumMember(decl) && decl.initializer) {
|
|
66
|
+
if (ts.isStringLiteral(decl.initializer)) {
|
|
67
|
+
return {
|
|
68
|
+
values: [decl.initializer.text],
|
|
69
|
+
dynamic: false,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
29
72
|
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
30
75
|
|
|
31
|
-
|
|
32
|
-
|
|
76
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
77
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
33
78
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
79
|
+
if (symbol) {
|
|
80
|
+
let s = symbol;
|
|
81
|
+
|
|
82
|
+
if (s.flags & ts.SymbolFlags.Alias) {
|
|
83
|
+
s = checker.getAliasedSymbol(s);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const decl of s.getDeclarations() ?? []) {
|
|
87
|
+
if (ts.isEnumMember(decl) && decl.initializer) {
|
|
88
|
+
if (ts.isStringLiteral(decl.initializer)) {
|
|
89
|
+
return {
|
|
90
|
+
values: [decl.initializer.text],
|
|
91
|
+
dynamic: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
37
94
|
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
100
|
+
const values: string[] = [];
|
|
101
|
+
|
|
102
|
+
for (const prop of node.properties) {
|
|
103
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
38
104
|
|
|
39
|
-
|
|
105
|
+
const name = prop.name.getText();
|
|
106
|
+
|
|
107
|
+
if (name === "event") {
|
|
108
|
+
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
109
|
+
if (res) values.push(...res.values);
|
|
40
110
|
}
|
|
41
111
|
}
|
|
42
112
|
|
|
43
|
-
|
|
44
|
-
|
|
113
|
+
return { values, dynamic: true };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (ts.isBinaryExpression(node)) {
|
|
117
|
+
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
118
|
+
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
119
|
+
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
45
120
|
|
|
46
|
-
if (
|
|
47
|
-
return {
|
|
121
|
+
if (left && right) {
|
|
122
|
+
return {
|
|
123
|
+
values: left.values.flatMap(l =>
|
|
124
|
+
right.values.map(r => l + r)
|
|
125
|
+
),
|
|
126
|
+
dynamic: true,
|
|
127
|
+
};
|
|
48
128
|
}
|
|
49
129
|
}
|
|
50
130
|
}
|
package/src/engine/scanner.ts
CHANGED
|
@@ -1,90 +1,83 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { resolveNodeValue } from "./resolver";
|
|
3
3
|
import { resolveFunctionFromCall } from "./callResolver";
|
|
4
|
-
import { getForwardedParamIndex } from "./flow";
|
|
5
|
-
|
|
6
|
-
function getCallName(expr: Node): string {
|
|
7
|
-
if (Node.isIdentifier(expr)) return expr.getText();
|
|
8
|
-
if (Node.isPropertyAccessExpression(expr)) return expr.getName();
|
|
9
|
-
return expr.getText();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function isTrackLike(name: string) {
|
|
13
|
-
return /(track|trackFeature)/i.test(name);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function pickArg(args: Node[]) {
|
|
17
|
-
return (
|
|
18
|
-
args.find(Node.isStringLiteral) ??
|
|
19
|
-
args.find(Node.isTemplateExpression) ??
|
|
20
|
-
args.find(Node.isObjectLiteralExpression) ??
|
|
21
|
-
args[0]
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
4
|
|
|
25
5
|
export function scanSource(
|
|
26
|
-
source: SourceFile,
|
|
27
|
-
checker: ts.TypeChecker
|
|
28
|
-
)
|
|
6
|
+
source: ts.SourceFile,
|
|
7
|
+
checker: ts.TypeChecker,
|
|
8
|
+
visited = new Set<string>(),
|
|
9
|
+
depth = 0
|
|
10
|
+
): Set<string> {
|
|
11
|
+
if (depth > 5) return new Set();
|
|
29
12
|
const events = new Set<string>();
|
|
30
13
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
14
|
+
if (visited.has(source.fileName)) return events;
|
|
15
|
+
visited.add(source.fileName);
|
|
16
|
+
|
|
17
|
+
function visit(node: ts.Node) {
|
|
18
|
+
if (ts.isCallExpression(node)) {
|
|
19
|
+
const name = getCallName(node.expression);
|
|
20
|
+
|
|
21
|
+
if (/(track|event|capture|send)/i.test(name)) {
|
|
22
|
+
|
|
23
|
+
const fn = resolveFunctionFromCall(node.expression, checker);
|
|
24
|
+
|
|
25
|
+
// param mapping
|
|
26
|
+
let paramMap = new Map<string, ts.Expression>();
|
|
27
|
+
|
|
28
|
+
if (fn && ts.isFunctionLike(fn)) {
|
|
29
|
+
fn.parameters.forEach((param, index) => {
|
|
30
|
+
const arg = node.arguments[index];
|
|
31
|
+
if (!arg) return;
|
|
32
|
+
|
|
33
|
+
if (ts.isIdentifier(param.name)) {
|
|
34
|
+
paramMap.set(param.name.text, arg);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// cross-file traversal
|
|
40
|
+
if (fn) {
|
|
41
|
+
const sf = fn.getSourceFile();
|
|
42
|
+
|
|
43
|
+
if (!visited.has(sf.fileName)) {
|
|
44
|
+
scanSource(sf, checker, visited, depth + 1)
|
|
45
|
+
.forEach(e => events.add(e));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const arg of node.arguments) {
|
|
50
|
+
const res = resolveNodeValue(arg, checker, paramMap);
|
|
51
|
+
|
|
52
|
+
res?.values.forEach(v => {
|
|
53
|
+
if (
|
|
54
|
+
v &&
|
|
55
|
+
v.length > 1 &&
|
|
56
|
+
v.length < 100 &&
|
|
57
|
+
!v.includes(" ") &&
|
|
58
|
+
/^[a-zA-Z0-9._:-]+$/.test(v)
|
|
59
|
+
) {
|
|
60
|
+
events.add(v);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
49
64
|
}
|
|
50
65
|
}
|
|
51
66
|
|
|
52
|
-
|
|
53
|
-
if (isValid(value)) events.add(value);
|
|
54
|
-
});
|
|
67
|
+
ts.forEachChild(node, visit);
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
const jsx = [
|
|
59
|
-
...source.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
|
|
60
|
-
...source.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
for (const el of jsx) {
|
|
64
|
-
for (const attr of el.getAttributes()) {
|
|
65
|
-
if (!Node.isJsxAttribute(attr)) continue;
|
|
66
|
-
|
|
67
|
-
const name = attr.getNameNode().getText();
|
|
68
|
-
if (!/(event|name|action)/i.test(name)) continue;
|
|
70
|
+
visit(source);
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const expr = Node.isJsxExpression(init)
|
|
74
|
-
? init.getExpression()
|
|
75
|
-
: init;
|
|
72
|
+
return events;
|
|
73
|
+
}
|
|
76
74
|
|
|
77
|
-
|
|
75
|
+
function getCallName(expr: ts.Expression): string {
|
|
76
|
+
if (ts.isIdentifier(expr)) return expr.text;
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
}
|
|
78
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
79
|
+
return expr.name.text;
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function isValid(v: string) {
|
|
89
|
-
return !!v && v.length > 1 && !v.includes(" ");
|
|
82
|
+
return "";
|
|
90
83
|
}
|
package/src/types.ts
CHANGED
package/src/utils/config.ts
CHANGED
|
@@ -12,8 +12,7 @@ export function normalizeConfig(
|
|
|
12
12
|
events: config.events ?? [],
|
|
13
13
|
endpoint: config.endpoint ?? "",
|
|
14
14
|
wrappers: config.wrappers ?? [],
|
|
15
|
-
functionWrappers:
|
|
16
|
-
config.functionWrappers ?? [],
|
|
15
|
+
functionWrappers: config.functionWrappers ?? [],
|
|
17
16
|
aliases: config.aliases ?? {},
|
|
18
17
|
sync: config.sync ?? {
|
|
19
18
|
include: [
|
|
@@ -30,43 +29,26 @@ export function normalizeConfig(
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
export async function loadConfig(): Promise<EventraConfig | null> {
|
|
33
|
-
const configPath = path.join(
|
|
34
|
-
process.cwd(),
|
|
35
|
-
CONFIG_NAME
|
|
36
|
-
);
|
|
32
|
+
const configPath = path.join(process.cwd(), CONFIG_NAME);
|
|
37
33
|
|
|
38
|
-
if (
|
|
39
|
-
!(await fs.pathExists(
|
|
40
|
-
configPath
|
|
41
|
-
))
|
|
42
|
-
) {
|
|
34
|
+
if (!(await fs.pathExists(configPath))) {
|
|
43
35
|
return null;
|
|
44
36
|
}
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
await fs.readJSON(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
try {
|
|
39
|
+
const config = await fs.readJSON(configPath);
|
|
40
|
+
return normalizeConfig(config);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
52
44
|
}
|
|
53
45
|
|
|
54
|
-
export async function saveConfig(
|
|
55
|
-
|
|
56
|
-
) {
|
|
57
|
-
const configPath = path.join(
|
|
58
|
-
process.cwd(),
|
|
59
|
-
CONFIG_NAME
|
|
60
|
-
);
|
|
46
|
+
export async function saveConfig(config: EventraConfig) {
|
|
47
|
+
const configPath = path.join(process.cwd(), CONFIG_NAME);
|
|
61
48
|
|
|
62
|
-
const normalized =
|
|
63
|
-
normalizeConfig(config);
|
|
49
|
+
const normalized = normalizeConfig(config);
|
|
64
50
|
|
|
65
|
-
await fs.writeJSON(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
spaces: 2
|
|
70
|
-
}
|
|
71
|
-
);
|
|
51
|
+
await fs.writeJSON(configPath, normalized, {
|
|
52
|
+
spaces: 2
|
|
53
|
+
});
|
|
72
54
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
export function parseAstro(
|
|
2
|
-
content
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export function parseAstro(content: string) {
|
|
2
|
+
const frontmatter = content.match(/---([\s\S]*?)---/);
|
|
3
|
+
|
|
4
|
+
const scripts = [
|
|
5
|
+
...content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/g)
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
return [
|
|
9
|
+
frontmatter?.[1] ?? "",
|
|
10
|
+
...scripts.map(s => s[1])
|
|
11
|
+
].join("\n");
|
|
5
12
|
}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
export function detectParser(
|
|
2
|
-
file
|
|
3
|
-
) {
|
|
4
|
-
if (file.endsWith(".vue"))
|
|
5
|
-
return "vue";
|
|
1
|
+
export function detectParser(file: string) {
|
|
2
|
+
file = file.toLowerCase();
|
|
6
3
|
|
|
7
|
-
if (file.endsWith(".
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (file.endsWith(".astro"))
|
|
11
|
-
return "astro";
|
|
4
|
+
if (file.endsWith(".vue")) return "vue";
|
|
5
|
+
if (file.endsWith(".svelte")) return "svelte";
|
|
6
|
+
if (file.endsWith(".astro")) return "astro";
|
|
12
7
|
|
|
13
8
|
return "ts";
|
|
14
9
|
}
|