@eventra_dev/eventra-cli 0.1.24 → 0.1.26

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.
@@ -5,21 +5,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.resolveExportedSymbol = resolveExportedSymbol;
7
7
  const typescript_1 = __importDefault(require("typescript"));
8
+ function isRealDeclaration(node) {
9
+ return (typescript_1.default.isFunctionDeclaration(node) ||
10
+ typescript_1.default.isVariableDeclaration(node) ||
11
+ typescript_1.default.isClassDeclaration(node));
12
+ }
8
13
  function resolveExportedSymbol(symbol, checker, visited = new Set()) {
9
14
  if (visited.has(symbol))
10
15
  return null;
11
16
  visited.add(symbol);
17
+ // unwrap alias
12
18
  if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
13
19
  const aliased = checker.getAliasedSymbol(symbol);
14
20
  return resolveExportedSymbol(aliased, checker, visited);
15
21
  }
16
- if (symbol.getDeclarations()?.length) {
22
+ // export symbol
23
+ const exportSymbol = checker.getExportSymbolOfSymbol(symbol);
24
+ if (exportSymbol && exportSymbol !== symbol) {
25
+ return resolveExportedSymbol(exportSymbol, checker, visited);
26
+ }
27
+ const decls = symbol.getDeclarations() ?? [];
28
+ if (decls.some(isRealDeclaration)) {
17
29
  return symbol;
18
30
  }
19
- const declarations = symbol.getDeclarations() ?? [];
20
- for (const decl of declarations) {
31
+ // exports (re-export)
32
+ for (const decl of decls) {
21
33
  const source = decl.getSourceFile();
22
- const moduleSymbol = checker.getSymbolAtLocation(source);
34
+ const moduleSymbol = source.symbol;
23
35
  if (!moduleSymbol)
24
36
  continue;
25
37
  const exports = checker.getExportsOfModule(moduleSymbol);
@@ -10,19 +10,81 @@ function findTrackParamIndex(fn) {
10
10
  fn.parameters.forEach((p, i) => {
11
11
  if (typescript_1.default.isIdentifier(p.name)) {
12
12
  paramIndex.set(p.name.text, i);
13
+ // default param: (name = "x")
14
+ if (p.initializer && typescript_1.default.isStringLiteral(p.initializer)) {
15
+ if (i === 0)
16
+ return 0;
17
+ }
13
18
  }
14
19
  });
15
20
  let result = null;
21
+ function unwrap(node) {
22
+ // as / parentheses
23
+ while (typescript_1.default.isAsExpression(node) ||
24
+ typescript_1.default.isParenthesizedExpression(node)) {
25
+ node = node.expression;
26
+ }
27
+ return node;
28
+ }
29
+ function checkArg(arg) {
30
+ arg = unwrap(arg);
31
+ // identifier → param
32
+ if (typescript_1.default.isIdentifier(arg)) {
33
+ const idx = paramIndex.get(arg.text);
34
+ if (idx !== undefined) {
35
+ result = idx;
36
+ return true;
37
+ }
38
+ }
39
+ // string literal
40
+ if (typescript_1.default.isStringLiteral(arg)) {
41
+ result = 0;
42
+ return true;
43
+ }
44
+ // template `${name}`
45
+ if (typescript_1.default.isTemplateExpression(arg)) {
46
+ for (const span of arg.templateSpans) {
47
+ if (typescript_1.default.isIdentifier(span.expression)) {
48
+ const idx = paramIndex.get(span.expression.text);
49
+ if (idx !== undefined) {
50
+ result = idx;
51
+ return true;
52
+ }
53
+ }
54
+ }
55
+ result = 0;
56
+ return true;
57
+ }
58
+ // object { event: name }
59
+ if (typescript_1.default.isObjectLiteralExpression(arg)) {
60
+ for (const prop of arg.properties) {
61
+ if (!typescript_1.default.isPropertyAssignment(prop))
62
+ continue;
63
+ const name = prop.name.getText();
64
+ if (["event", "name", "type"].includes(name)) {
65
+ return checkArg(prop.initializer);
66
+ }
67
+ }
68
+ }
69
+ // call: String(name)
70
+ if (typescript_1.default.isCallExpression(arg)) {
71
+ for (const inner of arg.arguments) {
72
+ if (checkArg(inner))
73
+ return true;
74
+ }
75
+ }
76
+ return false;
77
+ }
16
78
  function visit(node) {
79
+ if (result !== null)
80
+ return;
17
81
  if (typescript_1.default.isCallExpression(node)) {
18
82
  let isTrackCall = false;
19
83
  if (typescript_1.default.isPropertyAccessExpression(node.expression)) {
20
- const method = node.expression.name.text;
21
- if (method === "track") {
84
+ if (node.expression.name.text === "track") {
22
85
  isTrackCall = true;
23
86
  }
24
87
  }
25
- // fallback
26
88
  if (typescript_1.default.isIdentifier(node.expression)) {
27
89
  if (node.expression.text === "track") {
28
90
  isTrackCall = true;
@@ -30,17 +92,8 @@ function findTrackParamIndex(fn) {
30
92
  }
31
93
  if (isTrackCall) {
32
94
  for (const arg of node.arguments) {
33
- if (typescript_1.default.isIdentifier(arg)) {
34
- const idx = paramIndex.get(arg.text);
35
- if (idx !== undefined) {
36
- result = idx;
37
- return;
38
- }
39
- }
40
- if (typescript_1.default.isStringLiteral(arg)) {
41
- result = 0;
95
+ if (checkArg(arg))
42
96
  return;
43
- }
44
97
  }
45
98
  }
46
99
  }
@@ -18,13 +18,16 @@ class DependencyGraph {
18
18
  this.paths = paths;
19
19
  }
20
20
  normalize(file) {
21
- return path_1.default.resolve(file);
21
+ return path_1.default.resolve(file).replace(/\\/g, "/");
22
22
  }
23
+ // MODULE RESOLVE
23
24
  resolveModule(module, from) {
25
+ // relative import
24
26
  if (module.startsWith(".")) {
25
27
  const abs = path_1.default.resolve(path_1.default.dirname(from), module);
26
28
  return this.resolveFile(abs);
27
29
  }
30
+ // tsconfig paths
28
31
  for (const key in this.paths) {
29
32
  const pattern = key.replace(/\*/g, "(.*)");
30
33
  const regex = new RegExp("^" + pattern + "$");
@@ -43,9 +46,19 @@ class DependencyGraph {
43
46
  return resolved;
44
47
  }
45
48
  }
49
+ // node resolution (safe)
50
+ try {
51
+ const resolved = require.resolve(module, {
52
+ paths: [path_1.default.dirname(from)],
53
+ });
54
+ return this.normalize(resolved);
55
+ }
56
+ catch { }
57
+ // fallback
46
58
  const abs = path_1.default.resolve(this.baseUrl, module);
47
59
  return this.resolveFile(abs);
48
60
  }
61
+ // FILE RESOLVE
49
62
  resolveFile(base) {
50
63
  if (this.fileCache.has(base)) {
51
64
  return this.fileCache.get(base);
@@ -56,6 +69,12 @@ class DependencyGraph {
56
69
  base + ".tsx",
57
70
  base + ".js",
58
71
  base + ".jsx",
72
+ base + ".mjs",
73
+ base + ".cjs",
74
+ base + ".d.ts",
75
+ base + ".vue",
76
+ base + ".svelte",
77
+ base + ".astro",
59
78
  path_1.default.join(base, "index.ts"),
60
79
  path_1.default.join(base, "index.tsx"),
61
80
  path_1.default.join(base, "index.js"),
@@ -70,12 +89,22 @@ class DependencyGraph {
70
89
  this.fileCache.set(base, null);
71
90
  return null;
72
91
  }
92
+ // UPDATE GRAPH
73
93
  update(file, source) {
74
94
  const normalizedFile = this.normalize(file);
95
+ for (const deps of this.reverse.values()) {
96
+ deps.delete(normalizedFile);
97
+ }
75
98
  this.exports.set(normalizedFile, new Set());
76
99
  for (const stmt of source.statements) {
100
+ // IMPORT
77
101
  if (typescript_1.default.isImportDeclaration(stmt)) {
78
- const module = stmt.moduleSpecifier.getText().replace(/['"]/g, "");
102
+ // ignore import type
103
+ if (stmt.importClause?.importKind === "type")
104
+ continue;
105
+ const module = stmt.moduleSpecifier
106
+ .getText()
107
+ .replace(/['"]/g, "");
79
108
  const resolved = this.resolveModule(module, normalizedFile);
80
109
  if (!resolved)
81
110
  continue;
@@ -84,8 +113,13 @@ class DependencyGraph {
84
113
  }
85
114
  this.reverse.get(resolved).add(normalizedFile);
86
115
  }
87
- if (typescript_1.default.isExportDeclaration(stmt) && stmt.moduleSpecifier) {
88
- const module = stmt.moduleSpecifier.getText().replace(/['"]/g, "");
116
+ // EXPORT (re-export)
117
+ if (typescript_1.default.isExportDeclaration(stmt)) {
118
+ if (!stmt.moduleSpecifier)
119
+ continue;
120
+ const module = stmt.moduleSpecifier
121
+ .getText()
122
+ .replace(/['"]/g, "");
89
123
  const resolved = this.resolveModule(module, normalizedFile);
90
124
  if (!resolved)
91
125
  continue;
@@ -97,6 +131,7 @@ class DependencyGraph {
97
131
  }
98
132
  }
99
133
  }
134
+ // DEPENDENTS
100
135
  getAllDependentsDeep(file) {
101
136
  const start = this.normalize(file);
102
137
  const visited = new Set();
@@ -116,6 +151,7 @@ class DependencyGraph {
116
151
  walk(start);
117
152
  return [...result];
118
153
  }
154
+ // REMOVE
119
155
  remove(file) {
120
156
  const normalized = this.normalize(file);
121
157
  this.reverse.delete(normalized);
@@ -12,20 +12,27 @@ class TSService {
12
12
  files = new Map();
13
13
  fileNames = new Set();
14
14
  service;
15
- compilerOptions = {};
16
- baseUrl = "";
17
- paths = {};
15
+ compilerOptions;
16
+ baseUrl;
17
+ paths;
18
+ usage = new Map();
19
+ tick = 0;
20
+ MAX_FILES = 2000;
18
21
  constructor(root) {
19
22
  this.root = root;
20
23
  const configPath = typescript_1.default.findConfigFile(root, typescript_1.default.sys.fileExists, "tsconfig.json");
21
24
  let compilerOptions = {
22
25
  allowJs: true,
23
- jsx: typescript_1.default.JsxEmit.Preserve,
26
+ jsx: typescript_1.default.JsxEmit.React,
27
+ jsxImportSource: "react",
24
28
  moduleResolution: typescript_1.default.ModuleResolutionKind.Node16,
25
29
  target: typescript_1.default.ScriptTarget.ESNext,
26
30
  esModuleInterop: true,
27
31
  allowSyntheticDefaultImports: true,
32
+ skipLibCheck: true,
28
33
  };
34
+ let baseUrl = root;
35
+ let paths = {};
29
36
  if (configPath) {
30
37
  const configFile = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
31
38
  const parsed = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, root);
@@ -33,46 +40,104 @@ class TSService {
33
40
  ...compilerOptions,
34
41
  ...parsed.options,
35
42
  };
36
- this.paths = parsed.options.paths || {};
37
- this.baseUrl = parsed.options.baseUrl
43
+ paths = parsed.options.paths || {};
44
+ baseUrl = parsed.options.baseUrl
38
45
  ? path_1.default.resolve(root, parsed.options.baseUrl)
39
46
  : root;
40
47
  }
41
- else {
42
- this.baseUrl = root;
43
- }
44
48
  this.compilerOptions = compilerOptions;
49
+ this.baseUrl = baseUrl;
50
+ this.paths = paths;
45
51
  const host = {
46
- getScriptFileNames: () => {
47
- return [...new Set(this.fileNames)].sort((a, b) => a.localeCompare(b));
52
+ getScriptFileNames: () => Array.from(this.fileNames),
53
+ getScriptVersion: (file) => {
54
+ file = this.normalize(file);
55
+ return this.files.get(file)?.version.toString() ?? "0";
48
56
  },
49
- getScriptVersion: (file) => this.files.get(file)?.version.toString() ?? "0",
50
57
  getScriptSnapshot: (file) => {
58
+ file = this.normalize(file);
51
59
  const f = this.files.get(file);
52
60
  if (f) {
61
+ this.touch(file);
53
62
  return typescript_1.default.ScriptSnapshot.fromString(f.content);
54
63
  }
55
- if (fs_1.default.existsSync(file)) {
56
- return typescript_1.default.ScriptSnapshot.fromString(fs_1.default.readFileSync(file, "utf-8"));
64
+ try {
65
+ if (fs_1.default.existsSync(file)) {
66
+ const content = fs_1.default.readFileSync(file, "utf-8");
67
+ this.files.set(file, {
68
+ content,
69
+ version: 1,
70
+ });
71
+ this.fileNames.add(file);
72
+ this.touch(file);
73
+ this.evictIfNeeded();
74
+ return typescript_1.default.ScriptSnapshot.fromString(content);
75
+ }
57
76
  }
58
- return undefined;
77
+ catch { }
78
+ // fallback
79
+ return typescript_1.default.ScriptSnapshot.fromString("");
59
80
  },
60
81
  getCurrentDirectory: () => this.root,
61
82
  getCompilationSettings: () => this.compilerOptions,
62
83
  getDefaultLibFileName: (opts) => typescript_1.default.getDefaultLibFilePath(opts),
63
- readFile: typescript_1.default.sys.readFile,
64
84
  fileExists: typescript_1.default.sys.fileExists,
85
+ readFile: typescript_1.default.sys.readFile,
65
86
  readDirectory: typescript_1.default.sys.readDirectory,
66
87
  };
67
88
  this.service = typescript_1.default.createLanguageService(host);
68
89
  }
90
+ // INTERNAL
91
+ normalize(file) {
92
+ return path_1.default.resolve(file).replace(/\\/g, "/");
93
+ }
94
+ touch(file) {
95
+ this.tick++;
96
+ this.usage.set(file, this.tick);
97
+ }
98
+ evictIfNeeded() {
99
+ if (this.fileNames.size <= this.MAX_FILES)
100
+ return;
101
+ let oldestFile = null;
102
+ let oldestTick = Infinity;
103
+ for (const f of this.fileNames) {
104
+ const t = this.usage.get(f) ?? 0;
105
+ if (t < oldestTick) {
106
+ oldestTick = t;
107
+ oldestFile = f;
108
+ }
109
+ }
110
+ if (oldestFile) {
111
+ this.files.delete(oldestFile);
112
+ this.fileNames.delete(oldestFile);
113
+ this.usage.delete(oldestFile);
114
+ }
115
+ }
116
+ ensureProgram() {
117
+ try {
118
+ this.service.getProgram();
119
+ }
120
+ catch { }
121
+ }
122
+ // PUBLIC API
69
123
  updateFile(file, content) {
124
+ file = this.normalize(file);
70
125
  const prev = this.files.get(file);
71
126
  this.files.set(file, {
72
127
  content,
73
128
  version: prev ? prev.version + 1 : 1,
74
129
  });
75
130
  this.fileNames.add(file);
131
+ this.touch(file);
132
+ this.evictIfNeeded();
133
+ this.ensureProgram();
134
+ }
135
+ removeFile(file) {
136
+ file = this.normalize(file);
137
+ this.files.delete(file);
138
+ this.fileNames.delete(file);
139
+ this.usage.delete(file);
140
+ this.ensureProgram();
76
141
  }
77
142
  getProgram() {
78
143
  return this.service.getProgram();
@@ -81,15 +146,13 @@ class TSService {
81
146
  return this.getProgram()?.getTypeChecker();
82
147
  }
83
148
  getSourceFile(file) {
149
+ file = this.normalize(file);
84
150
  return this.getProgram()?.getSourceFile(file);
85
151
  }
86
152
  getFileContent(file) {
153
+ file = this.normalize(file);
87
154
  return this.files.get(file)?.content;
88
155
  }
89
- removeFile(file) {
90
- this.files.delete(file);
91
- this.fileNames.delete(file);
92
- }
93
156
  getModuleResolutionConfig() {
94
157
  return {
95
158
  baseUrl: this.baseUrl,
@@ -11,7 +11,7 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
11
11
  return { values: [], dynamic: true };
12
12
  }
13
13
  seen.add(node);
14
- // param mapping
14
+ // PARAM MAPPING
15
15
  if (typescript_1.default.isIdentifier(node) && paramMap?.has(node.text)) {
16
16
  return resolveNodeValue(paramMap.get(node.text), checker, paramMap, seen);
17
17
  }
@@ -28,11 +28,14 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
28
28
  values.push(...res.values);
29
29
  }
30
30
  }
31
- return { values, dynamic: true };
31
+ return {
32
+ values: [...new Set(values)],
33
+ dynamic: true
34
+ };
32
35
  }
33
36
  // TEMPLATE
34
37
  if (typescript_1.default.isTemplateExpression(node)) {
35
- let results = [node.head.text];
38
+ let results = node.head.text ? [node.head.text] : [""];
36
39
  for (const span of node.templateSpans) {
37
40
  const res = resolveNodeValue(span.expression, checker, paramMap, seen);
38
41
  if (res.values.length) {
@@ -59,16 +62,18 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
59
62
  let symbol = checker.getSymbolAtLocation(node);
60
63
  if (!symbol)
61
64
  return { values: [], dynamic: true };
65
+ // INLINE FIRST
66
+ for (const decl of symbol.getDeclarations() ?? []) {
67
+ if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
68
+ return resolveNodeValue(decl.initializer, checker, paramMap, seen);
69
+ }
70
+ }
62
71
  // unwrap export
63
72
  symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
64
73
  if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
65
74
  symbol = checker.getAliasedSymbol(symbol);
66
75
  }
67
- // inline variable
68
76
  for (const decl of symbol.getDeclarations() ?? []) {
69
- if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
70
- return resolveNodeValue(decl.initializer, checker, paramMap, seen);
71
- }
72
77
  if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
73
78
  if (typescript_1.default.isStringLiteral(decl.initializer)) {
74
79
  return {
@@ -78,12 +83,12 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
78
83
  }
79
84
  }
80
85
  }
81
- // UNION TYPES
82
86
  const type = checker.getTypeAtLocation(node);
87
+ // UNION
83
88
  if (type.isUnion()) {
84
89
  const values = [];
85
90
  for (const t of type.types) {
86
- if (t.isStringLiteral?.()) {
91
+ if (t.flags & typescript_1.default.TypeFlags.StringLiteral) {
87
92
  values.push(t.value);
88
93
  }
89
94
  }
@@ -91,13 +96,16 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
91
96
  return { values, dynamic: false };
92
97
  }
93
98
  }
94
- // TYPE FALLBACK (as const)
95
- if (type.isStringLiteral?.()) {
96
- return { values: [type.value], dynamic: false };
99
+ // STRING LITERAL TYPE
100
+ if (type.flags & typescript_1.default.TypeFlags.StringLiteral) {
101
+ return {
102
+ values: [type.value],
103
+ dynamic: false
104
+ };
97
105
  }
98
106
  return { values: [], dynamic: true };
99
107
  }
100
- // PROPERTY ACCESS (obj.a.b)
108
+ // PROPERTY ACCESS
101
109
  if (typescript_1.default.isPropertyAccessExpression(node)) {
102
110
  const symbol = checker.getSymbolAtLocation(node);
103
111
  if (symbol) {
@@ -116,14 +124,12 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
116
124
  }
117
125
  }
118
126
  }
119
- // constant enum / readonly
120
127
  const constantValue = checker.getConstantValue(node);
121
128
  if (typeof constantValue === "string") {
122
129
  return { values: [constantValue], dynamic: false };
123
130
  }
124
- // type fallback
125
131
  const type = checker.getTypeAtLocation(node);
126
- if (type.isStringLiteral?.()) {
132
+ if (type.flags & typescript_1.default.TypeFlags.StringLiteral) {
127
133
  return {
128
134
  values: [type.value],
129
135
  dynamic: false,
@@ -149,16 +155,16 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
149
155
  }
150
156
  }
151
157
  }
152
- return { values: [], dynamic: true };
158
+ // fallback
159
+ return resolveNodeValue(node.name, checker, paramMap, seen);
153
160
  }
154
- // ARRAY ACCESS (arr[0])
161
+ // ARRAY ACCESS
155
162
  if (typescript_1.default.isElementAccessExpression(node)) {
156
163
  const obj = resolveNodeValue(node.expression, checker, paramMap, seen);
157
164
  if (!node.argumentExpression) {
158
165
  return { values: obj.values, dynamic: true };
159
166
  }
160
167
  const index = resolveNodeValue(node.argumentExpression, checker, paramMap, seen);
161
- // if index known
162
168
  if (index.values.length === 1) {
163
169
  const i = Number(index.values[0]);
164
170
  if (!Number.isNaN(i) && obj.values[i] !== undefined) {
@@ -168,7 +174,6 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
168
174
  };
169
175
  }
170
176
  }
171
- // fallback → dynamic
172
177
  return obj.values.length
173
178
  ? { values: obj.values, dynamic: true }
174
179
  : { values: [], dynamic: true };
@@ -185,7 +190,10 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
185
190
  values.push(...res.values);
186
191
  }
187
192
  }
188
- return { values, dynamic: true };
193
+ return {
194
+ values: [...new Set(values)],
195
+ dynamic: true
196
+ };
189
197
  }
190
198
  // BINARY ("btn_" + name)
191
199
  if (typescript_1.default.isBinaryExpression(node)) {