@eventra_dev/eventra-cli 0.1.32 → 0.1.34

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.
@@ -7,13 +7,9 @@ export class TSService {
7
7
  private fileNames = new Set<string>();
8
8
  private service: ts.LanguageService;
9
9
 
10
- private readonly compilerOptions: ts.CompilerOptions;
11
- private readonly baseUrl: string;
12
- private readonly paths: Record<string, string[]>;
13
-
14
- private usage = new Map<string, number>();
15
- private tick = 0;
16
- private MAX_FILES = 2000;
10
+ private compilerOptions: ts.CompilerOptions;
11
+ private baseUrl: string;
12
+ private paths: Record<string, string[]>;
17
13
 
18
14
  constructor(private root: string) {
19
15
  const configPath = ts.findConfigFile(
@@ -61,16 +57,19 @@ export class TSService {
61
57
  this.baseUrl = baseUrl;
62
58
  this.paths = paths;
63
59
 
60
+ const allFiles = ts.sys.readDirectory(
61
+ root,
62
+ [".ts", ".tsx", ".js", ".jsx"],
63
+ undefined,
64
+ undefined
65
+ );
66
+
67
+ for (const f of allFiles) {
68
+ this.fileNames.add(this.normalize(f));
69
+ }
70
+
64
71
  const host: ts.LanguageServiceHost = {
65
72
  getScriptFileNames: () => {
66
- if (this.fileNames.size === 0) {
67
- return ts.sys.readDirectory(
68
- this.root,
69
- [".ts", ".tsx", ".js", ".jsx"],
70
- undefined,
71
- undefined
72
- );
73
- }
74
73
  return Array.from(this.fileNames);
75
74
  },
76
75
 
@@ -81,11 +80,14 @@ export class TSService {
81
80
 
82
81
  getScriptSnapshot: (file) => {
83
82
  file = this.normalize(file);
83
+
84
+ // из памяти
84
85
  const f = this.files.get(file);
85
86
  if (f) {
86
- this.touch(file);
87
87
  return ts.ScriptSnapshot.fromString(f.content);
88
88
  }
89
+
90
+ // virtual TSX (vue/svelte/astro)
89
91
  if (file.endsWith(".tsx")) {
90
92
  const original = file.replace(/\.tsx$/, "");
91
93
 
@@ -98,12 +100,11 @@ export class TSService {
98
100
  });
99
101
 
100
102
  this.fileNames.add(file);
101
- this.touch(file);
102
- this.evictIfNeeded();
103
103
 
104
104
  return ts.ScriptSnapshot.fromString(content);
105
105
  }
106
106
  }
107
+
107
108
  try {
108
109
  if (fs.existsSync(file)) {
109
110
  const content = fs.readFileSync(file, "utf-8");
@@ -114,8 +115,6 @@ export class TSService {
114
115
  });
115
116
 
116
117
  this.fileNames.add(file);
117
- this.touch(file);
118
- this.evictIfNeeded();
119
118
 
120
119
  return ts.ScriptSnapshot.fromString(content);
121
120
  }
@@ -123,10 +122,12 @@ export class TSService {
123
122
 
124
123
  return ts.ScriptSnapshot.fromString("");
125
124
  },
125
+
126
126
  getCurrentDirectory: () => this.root,
127
127
  getCompilationSettings: () => this.compilerOptions,
128
128
  getDefaultLibFileName: (opts) =>
129
129
  ts.getDefaultLibFilePath(opts),
130
+
130
131
  fileExists: (file) => {
131
132
  file = this.normalize(file);
132
133
  return this.files.has(file) || ts.sys.fileExists(file);
@@ -143,41 +144,13 @@ export class TSService {
143
144
  this.service = ts.createLanguageService(host);
144
145
  }
145
146
 
146
- // INTERNAL
147
147
  private normalize(file: string) {
148
148
  return path.resolve(file).replace(/\\/g, "/");
149
149
  }
150
150
 
151
- private touch(file: string) {
152
- this.tick++;
153
- this.usage.set(file, this.tick);
154
- }
155
-
156
- private evictIfNeeded() {
157
- if (this.fileNames.size <= this.MAX_FILES) return;
158
-
159
- let oldest: string | null = null;
160
- let oldestTick = Infinity;
161
-
162
- for (const f of this.fileNames) {
163
- const t = this.usage.get(f) ?? 0;
164
-
165
- if (t < oldestTick) {
166
- oldestTick = t;
167
- oldest = f;
168
- }
169
- }
170
-
171
- if (oldest) {
172
- this.files.delete(oldest);
173
- this.fileNames.delete(oldest);
174
- this.usage.delete(oldest);
175
- }
176
- }
177
-
178
151
  private ensureProgram() {
179
152
  try {
180
- this.service.getProgram()?.getTypeChecker();
153
+ this.service.getProgram();
181
154
  } catch {}
182
155
  }
183
156
 
@@ -193,8 +166,6 @@ export class TSService {
193
166
  });
194
167
 
195
168
  this.fileNames.add(file);
196
- this.touch(file);
197
- this.evictIfNeeded();
198
169
 
199
170
  this.ensureProgram();
200
171
  }
@@ -204,7 +175,6 @@ export class TSService {
204
175
 
205
176
  this.files.delete(file);
206
177
  this.fileNames.delete(file);
207
- this.usage.delete(file);
208
178
 
209
179
  this.ensureProgram();
210
180
  }
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
2
  import { resolveExportedSymbol } from "./exportResolver";
3
+ import { resolveFunctionFromCall } from "./callResolver";
3
4
 
4
5
  export type ResolveResult = {
5
6
  values: string[];
@@ -12,167 +13,139 @@ export function resolveNodeValue(
12
13
  paramMap?: Map<string, ts.Expression>,
13
14
  seen = new Set<ts.Node>()
14
15
  ): ResolveResult {
15
- if (seen.has(node)) {
16
- return { values: [], dynamic: true };
17
- }
16
+ if (seen.has(node)) return { values: [], dynamic: true };
18
17
  seen.add(node);
19
18
 
20
- // PARAM MAP (wrapper args)
19
+ // param passthrough
21
20
  if (ts.isIdentifier(node) && paramMap?.has(node.text)) {
22
- return resolveNodeValue(
23
- paramMap.get(node.text)!,
24
- checker,
25
- paramMap,
26
- seen
27
- );
21
+ return resolveNodeValue(paramMap.get(node.text)!, checker, paramMap, seen);
28
22
  }
29
23
 
30
- // STRING
24
+ // string
31
25
  if (ts.isStringLiteral(node)) {
32
26
  return { values: [node.text], dynamic: false };
33
27
  }
34
28
 
35
- // TEMPLATE
29
+ // template
36
30
  if (ts.isTemplateExpression(node)) {
37
31
  let results = node.head.text ? [node.head.text] : [""];
38
-
39
32
  for (const span of node.templateSpans) {
40
- const res = resolveNodeValue(
41
- span.expression,
42
- checker,
43
- paramMap,
44
- seen
45
- );
46
-
47
- results = results.flatMap(r =>
48
- res.values.length
49
- ? res.values.map(v => r + v)
50
- : [r + "*"]
33
+ const r = resolveNodeValue(span.expression, checker, paramMap, seen);
34
+ results = results.flatMap(s =>
35
+ r.values.length ? r.values.map(v => s + v) : [s + "*"]
51
36
  );
52
-
53
- results = results.map(r => r + span.literal.text);
37
+ results = results.map(s => s + span.literal.text);
54
38
  }
55
-
56
39
  return { values: results, dynamic: true };
57
40
  }
58
41
 
59
- // CONDITIONAL (a ? b : c)
42
+ // ternary
60
43
  if (ts.isConditionalExpression(node)) {
61
44
  const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
62
45
  const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
63
-
64
46
  return {
65
47
  values: [...new Set([...a.values, ...b.values])],
66
48
  dynamic: true,
67
49
  };
68
50
  }
69
51
 
70
- // OBJECT → берём ВСЕ значения (без привязки к ключам)
52
+ // object (event/name priority)
71
53
  if (ts.isObjectLiteralExpression(node)) {
72
- const values: string[] = [];
73
-
74
- for (const prop of node.properties) {
75
- if (!ts.isPropertyAssignment(prop)) continue;
76
-
77
- const res = resolveNodeValue(
78
- prop.initializer,
79
- checker,
80
- paramMap,
81
- seen
82
- );
83
-
84
- values.push(...res.values);
54
+ for (const p of node.properties) {
55
+ if (ts.isPropertyAssignment(p)) {
56
+ const key = p.name.getText();
57
+ if (key === "event" || key === "name") {
58
+ return resolveNodeValue(p.initializer, checker, paramMap, seen);
59
+ }
60
+ }
85
61
  }
86
-
87
- return {
88
- values: [...new Set(values)],
89
- dynamic: true,
90
- };
62
+ const vals: string[] = [];
63
+ for (const p of node.properties) {
64
+ if (!ts.isPropertyAssignment(p)) continue;
65
+ const r = resolveNodeValue(p.initializer, checker, paramMap, seen);
66
+ vals.push(...r.values);
67
+ }
68
+ return { values: [...new Set(vals)], dynamic: true };
91
69
  }
92
70
 
93
- // ARRAY
71
+ // array
94
72
  if (ts.isArrayLiteralExpression(node)) {
95
- const values: string[] = [];
96
-
73
+ const vals: string[] = [];
97
74
  for (const el of node.elements) {
98
- const res = resolveNodeValue(el, checker, paramMap, seen);
99
- values.push(...res.values);
75
+ const r = resolveNodeValue(el, checker, paramMap, seen);
76
+ vals.push(...r.values);
100
77
  }
78
+ return { values: [...new Set(vals)], dynamic: true };
79
+ }
101
80
 
81
+ // concat
82
+ if (ts.isBinaryExpression(node) &&
83
+ node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
84
+ const l = resolveNodeValue(node.left, checker, paramMap, seen);
85
+ const r = resolveNodeValue(node.right, checker, paramMap, seen);
102
86
  return {
103
- values: [...new Set(values)],
87
+ values: l.values.flatMap(a =>
88
+ r.values.length ? r.values.map(b => a + b) : [a + "*"]
89
+ ),
104
90
  dynamic: true,
105
91
  };
106
92
  }
107
93
 
108
- // BINARY CONCAT
109
- if (ts.isBinaryExpression(node)) {
110
- if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
111
- const left = resolveNodeValue(node.left, checker, paramMap, seen);
112
- const right = resolveNodeValue(node.right, checker, paramMap, seen);
113
-
114
- return {
115
- values: left.values.flatMap(l =>
116
- right.values.length
117
- ? right.values.map(r => l + r)
118
- : [l + "*"]
119
- ),
120
- dynamic: true,
121
- };
122
- }
123
- }
124
-
125
- // IDENTIFIER
94
+ // identifier (const / enum)
126
95
  if (ts.isIdentifier(node)) {
127
- let symbol = checker.getSymbolAtLocation(node);
128
- if (!symbol) return { values: [], dynamic: true };
129
-
130
- symbol = resolveExportedSymbol(symbol, checker) ?? symbol;
131
-
132
- // const VAR = "click"
133
- for (const decl of symbol.getDeclarations() ?? []) {
134
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
135
- return resolveNodeValue(
136
- decl.initializer,
137
- checker,
138
- paramMap,
139
- seen
140
- );
141
- }
96
+ let s = checker.getSymbolAtLocation(node);
97
+ if (!s) return { values: [], dynamic: true };
142
98
 
143
- // enum
144
- if (ts.isEnumMember(decl) && decl.initializer) {
145
- if (ts.isStringLiteral(decl.initializer)) {
146
- return {
147
- values: [decl.initializer.text],
148
- dynamic: false,
149
- };
150
- }
99
+ s = resolveExportedSymbol(s, checker) ?? s;
100
+
101
+ for (const d of s.getDeclarations() ?? []) {
102
+ if (ts.isVariableDeclaration(d) && d.initializer) {
103
+ return resolveNodeValue(d.initializer, checker, paramMap, seen);
104
+ }
105
+ if (ts.isEnumMember(d) && d.initializer && ts.isStringLiteral(d.initializer)) {
106
+ return { values: [d.initializer.text], dynamic: false };
151
107
  }
152
108
  }
153
-
154
109
  return { values: [], dynamic: true };
155
110
  }
156
111
 
157
- // PROPERTY ACCESS (ENUM / CONST)
112
+ // PROPERTY ACCESS (OBJECT + ENUM)
158
113
  if (ts.isPropertyAccessExpression(node)) {
159
- const symbol = checker.getSymbolAtLocation(node);
160
-
161
- if (symbol) {
162
- let s = resolveExportedSymbol(symbol, checker) ?? symbol;
163
-
164
- if (s.flags & ts.SymbolFlags.Alias) {
165
- s = checker.getAliasedSymbol(s);
114
+ const obj = node.expression;
115
+ const prop = node.name.text;
116
+
117
+ // object literal lookup
118
+ if (ts.isIdentifier(obj)) {
119
+ const sym = checker.getSymbolAtLocation(obj);
120
+ if (sym) {
121
+ const s = resolveExportedSymbol(sym, checker) ?? sym;
122
+ for (const d of s.getDeclarations() ?? []) {
123
+ if (
124
+ ts.isVariableDeclaration(d) &&
125
+ d.initializer &&
126
+ ts.isObjectLiteralExpression(d.initializer)
127
+ ) {
128
+ for (const p of d.initializer.properties) {
129
+ if (
130
+ ts.isPropertyAssignment(p) &&
131
+ p.name.getText() === prop
132
+ ) {
133
+ return resolveNodeValue(p.initializer, checker, paramMap, seen);
134
+ }
135
+ }
136
+ }
137
+ }
166
138
  }
139
+ }
167
140
 
168
- for (const decl of s.getDeclarations() ?? []) {
169
- if (ts.isEnumMember(decl) && decl.initializer) {
170
- if (ts.isStringLiteral(decl.initializer)) {
171
- return {
172
- values: [decl.initializer.text],
173
- dynamic: false,
174
- };
175
- }
141
+ // enum fallback
142
+ const sym = checker.getSymbolAtLocation(node);
143
+ if (sym) {
144
+ let s = resolveExportedSymbol(sym, checker) ?? sym;
145
+ if (s.flags & ts.SymbolFlags.Alias) s = checker.getAliasedSymbol(s);
146
+ for (const d of s.getDeclarations() ?? []) {
147
+ if (ts.isEnumMember(d) && d.initializer && ts.isStringLiteral(d.initializer)) {
148
+ return { values: [d.initializer.text], dynamic: false };
176
149
  }
177
150
  }
178
151
  }
@@ -180,18 +153,24 @@ export function resolveNodeValue(
180
153
  return { values: [], dynamic: true };
181
154
  }
182
155
 
183
- // CALL → unwrap args
156
+ // CALL (unwrap + dive into wrapper body)
184
157
  if (ts.isCallExpression(node)) {
185
- const values: string[] = [];
158
+ const vals: string[] = [];
159
+
160
+ // args
161
+ for (const a of node.arguments) {
162
+ const r = resolveNodeValue(a, checker, paramMap, seen);
163
+ vals.push(...r.values);
164
+ }
186
165
 
187
- for (const arg of node.arguments) {
188
- const res = resolveNodeValue(arg, checker, paramMap, seen);
189
- values.push(...res.values);
166
+ // dive into called function (wrapper support)
167
+ const fn = resolveFunctionFromCall(node.expression, checker);
168
+ if (fn?.body) {
169
+ const r = resolveNodeValue(fn.body, checker, paramMap, seen);
170
+ vals.push(...r.values);
190
171
  }
191
172
 
192
- return values.length
193
- ? { values, dynamic: true }
194
- : { values: [], dynamic: true };
173
+ return { values: [...new Set(vals)], dynamic: true };
195
174
  }
196
175
 
197
176
  return { values: [], dynamic: true };