@eventra_dev/eventra-cli 0.1.31 → 0.1.33
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/ARCHITECTURE.md +88 -0
- package/dist/engine/callResolver.js +56 -25
- package/dist/engine/languageService.js +36 -11
- package/dist/engine/scanner.js +74 -71
- package/dist/utils/universal.js +73 -21
- package/package.json +1 -1
- package/src/engine/callResolver.ts +61 -28
- package/src/engine/languageService.ts +50 -16
- package/src/engine/scanner.ts +83 -94
- package/src/utils/universal.ts +90 -21
package/dist/utils/universal.js
CHANGED
|
@@ -5,23 +5,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.parseUniversal = parseUniversal;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
|
|
8
|
+
/* ---------------------------------- */
|
|
9
|
+
/* COMMENT STRIP (SAFE) */
|
|
10
|
+
/* ---------------------------------- */
|
|
9
11
|
function stripComments(input) {
|
|
10
12
|
return input
|
|
11
13
|
.replace(/<!--[\s\S]*?-->/g, "")
|
|
12
14
|
.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
13
15
|
}
|
|
14
|
-
|
|
16
|
+
/* ---------------------------------- */
|
|
17
|
+
/* TRACK DETECTOR */
|
|
18
|
+
/* ---------------------------------- */
|
|
15
19
|
function looksLikeTracking(code) {
|
|
16
|
-
return /\btrack\
|
|
20
|
+
return /\btrack\s*\(|\.track\s*\(/.test(code);
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
/* ---------------------------------- */
|
|
23
|
+
/* SAFE {} PARSER (JSX / TEMPLATES) */
|
|
24
|
+
/* ---------------------------------- */
|
|
19
25
|
function extractBracedExpressions(input) {
|
|
20
26
|
const result = [];
|
|
21
27
|
let depth = 0;
|
|
22
28
|
let start = -1;
|
|
29
|
+
let inString = null;
|
|
23
30
|
for (let i = 0; i < input.length; i++) {
|
|
24
31
|
const char = input[i];
|
|
32
|
+
// handle strings
|
|
33
|
+
if (inString) {
|
|
34
|
+
if (char === inString && input[i - 1] !== "\\") {
|
|
35
|
+
inString = null;
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
40
|
+
inString = char;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
25
43
|
if (char === "{") {
|
|
26
44
|
if (depth === 0)
|
|
27
45
|
start = i + 1;
|
|
@@ -40,7 +58,9 @@ function extractBracedExpressions(input) {
|
|
|
40
58
|
}
|
|
41
59
|
return result;
|
|
42
60
|
}
|
|
43
|
-
|
|
61
|
+
/* ---------------------------------- */
|
|
62
|
+
/* HTML HANDLERS */
|
|
63
|
+
/* ---------------------------------- */
|
|
44
64
|
function extractHTMLHandlers(input) {
|
|
45
65
|
const result = [];
|
|
46
66
|
const regex = /\bon\w+\s*=\s*["']([^"']+)["']/g;
|
|
@@ -51,7 +71,9 @@ function extractHTMLHandlers(input) {
|
|
|
51
71
|
}
|
|
52
72
|
return result;
|
|
53
73
|
}
|
|
54
|
-
|
|
74
|
+
/* ---------------------------------- */
|
|
75
|
+
/* VUE HANDLERS */
|
|
76
|
+
/* ---------------------------------- */
|
|
55
77
|
function extractVueHandlers(input) {
|
|
56
78
|
const result = [];
|
|
57
79
|
const regex = /(?:@|v-on:)[\w:-]+\s*=\s*["']([^"']+)["']/g;
|
|
@@ -62,23 +84,27 @@ function extractVueHandlers(input) {
|
|
|
62
84
|
}
|
|
63
85
|
return result;
|
|
64
86
|
}
|
|
65
|
-
|
|
87
|
+
/* ---------------------------------- */
|
|
88
|
+
/* SVELTE HANDLERS */
|
|
89
|
+
/* ---------------------------------- */
|
|
66
90
|
function extractSvelteHandlers(input) {
|
|
67
91
|
const result = [];
|
|
68
|
-
const regex = /\bon:[\w]+\s*=\s*{([
|
|
92
|
+
const regex = /\bon:[\w]+\s*=\s*{([\s\S]*?)}/g;
|
|
69
93
|
for (const m of input.matchAll(regex)) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const expr = m[1];
|
|
95
|
+
const nested = extractBracedExpressions(`{${expr}}`);
|
|
96
|
+
result.push(...nested);
|
|
73
97
|
}
|
|
74
98
|
return result;
|
|
75
99
|
}
|
|
76
|
-
|
|
100
|
+
/* ---------------------------------- */
|
|
101
|
+
/* SCRIPT EXTRACTION */
|
|
102
|
+
/* ---------------------------------- */
|
|
77
103
|
function extractScripts(input, file) {
|
|
78
104
|
const scripts = [];
|
|
79
105
|
const deps = [];
|
|
80
|
-
// src
|
|
81
|
-
const srcRegex = /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/
|
|
106
|
+
// <script src="">
|
|
107
|
+
const srcRegex = /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/gi;
|
|
82
108
|
for (const m of input.matchAll(srcRegex)) {
|
|
83
109
|
const src = m[1];
|
|
84
110
|
const resolved = src.startsWith("/")
|
|
@@ -86,8 +112,8 @@ function extractScripts(input, file) {
|
|
|
86
112
|
: path_1.default.resolve(path_1.default.dirname(file), src);
|
|
87
113
|
deps.push(resolved);
|
|
88
114
|
}
|
|
89
|
-
// inline
|
|
90
|
-
const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/
|
|
115
|
+
// inline <script>
|
|
116
|
+
const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
|
|
91
117
|
for (const m of input.matchAll(scriptRegex)) {
|
|
92
118
|
if (m[1]?.trim()) {
|
|
93
119
|
scripts.push(m[1]);
|
|
@@ -96,9 +122,27 @@ function extractScripts(input, file) {
|
|
|
96
122
|
const noScripts = input.replace(scriptRegex, "");
|
|
97
123
|
return { scripts, deps, noScripts };
|
|
98
124
|
}
|
|
99
|
-
|
|
125
|
+
/* ---------------------------------- */
|
|
126
|
+
/* STATIC IMPORTS */
|
|
127
|
+
/* ---------------------------------- */
|
|
128
|
+
function extractStaticImports(input, file) {
|
|
129
|
+
const deps = [];
|
|
130
|
+
const regex = /import\s+(?:.+?\s+from\s+)?["'](.+?)["']/g;
|
|
131
|
+
for (const m of input.matchAll(regex)) {
|
|
132
|
+
const mod = m[1];
|
|
133
|
+
if (!mod.startsWith(".") && !mod.startsWith("/"))
|
|
134
|
+
continue;
|
|
135
|
+
const resolved = path_1.default.resolve(path_1.default.dirname(file), mod);
|
|
136
|
+
deps.push(resolved);
|
|
137
|
+
}
|
|
138
|
+
return deps;
|
|
139
|
+
}
|
|
140
|
+
/* ---------------------------------- */
|
|
141
|
+
/* DYNAMIC IMPORTS */
|
|
142
|
+
/* ---------------------------------- */
|
|
100
143
|
function extractDynamicImports(input, file) {
|
|
101
144
|
const deps = [];
|
|
145
|
+
const calls = [];
|
|
102
146
|
const regex = /import\(\s*["'](.+?)["']\s*\)/g;
|
|
103
147
|
for (const m of input.matchAll(regex)) {
|
|
104
148
|
const mod = m[1];
|
|
@@ -106,16 +150,21 @@ function extractDynamicImports(input, file) {
|
|
|
106
150
|
? path_1.default.resolve(process.cwd(), "." + mod)
|
|
107
151
|
: path_1.default.resolve(path_1.default.dirname(file), mod);
|
|
108
152
|
deps.push(resolved);
|
|
153
|
+
if (looksLikeTracking(mod)) {
|
|
154
|
+
calls.push(`import("${mod}")`);
|
|
155
|
+
}
|
|
109
156
|
}
|
|
110
|
-
return deps;
|
|
157
|
+
return { deps, calls };
|
|
111
158
|
}
|
|
112
|
-
|
|
159
|
+
/* ---------------------------------- */
|
|
160
|
+
/* MAIN */
|
|
161
|
+
/* ---------------------------------- */
|
|
113
162
|
function parseUniversal(content, file) {
|
|
114
163
|
const parts = [];
|
|
115
164
|
const deps = [];
|
|
116
165
|
const clean = stripComments(content);
|
|
117
166
|
/* ---------- ASTRO FRONTMATTER ---------- */
|
|
118
|
-
const fm = clean.match(
|
|
167
|
+
const fm = clean.match(/^\s*---([\s\S]*?)---/);
|
|
119
168
|
if (fm?.[1]) {
|
|
120
169
|
parts.push(fm[1]);
|
|
121
170
|
}
|
|
@@ -130,7 +179,10 @@ function parseUniversal(content, file) {
|
|
|
130
179
|
/* ---------- JSX / TEMPLATE ---------- */
|
|
131
180
|
parts.push(...extractBracedExpressions(clean));
|
|
132
181
|
/* ---------- IMPORTS ---------- */
|
|
133
|
-
deps.push(...
|
|
182
|
+
deps.push(...extractStaticImports(clean, file));
|
|
183
|
+
const dyn = extractDynamicImports(clean, file);
|
|
184
|
+
deps.push(...dyn.deps);
|
|
185
|
+
parts.push(...dyn.calls);
|
|
134
186
|
return {
|
|
135
187
|
code: [...new Set(parts)].join("\n"),
|
|
136
188
|
deps: [...new Set(deps.filter(Boolean))],
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
|
|
3
|
+
// HELPERS
|
|
3
4
|
function isFunctionLike(
|
|
4
5
|
node: ts.Node
|
|
5
6
|
): node is ts.FunctionLikeDeclaration {
|
|
@@ -12,40 +13,39 @@ function isFunctionLike(
|
|
|
12
13
|
);
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
// symbol → function
|
|
15
17
|
function resolveFromSymbol(
|
|
16
18
|
symbol: ts.Symbol,
|
|
17
19
|
checker: ts.TypeChecker,
|
|
18
20
|
visited = new Set<ts.Symbol>(),
|
|
19
21
|
depth = 0
|
|
20
22
|
): ts.FunctionLikeDeclaration | null {
|
|
23
|
+
if (!symbol) return null;
|
|
21
24
|
if (visited.has(symbol)) return null;
|
|
22
|
-
if (depth >
|
|
25
|
+
if (depth > 10) return null;
|
|
23
26
|
|
|
24
27
|
visited.add(symbol);
|
|
25
28
|
|
|
26
|
-
// unwrap
|
|
29
|
+
// Alias unwrap (import/export)
|
|
27
30
|
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// re-export support
|
|
33
|
-
if ((symbol as any).exports) {
|
|
34
|
-
for (const exp of (symbol as any).exports.values()) {
|
|
35
|
-
const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
|
|
36
|
-
if (resolved) return resolved;
|
|
37
|
-
}
|
|
31
|
+
try {
|
|
32
|
+
const aliased = checker.getAliasedSymbol(symbol);
|
|
33
|
+
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
34
|
+
} catch {}
|
|
38
35
|
}
|
|
39
36
|
|
|
37
|
+
// Declarations
|
|
40
38
|
const decls = symbol.getDeclarations() ?? [];
|
|
41
39
|
|
|
42
40
|
for (const decl of decls) {
|
|
43
|
-
|
|
41
|
+
/**
|
|
42
|
+
* direct function
|
|
43
|
+
*/
|
|
44
44
|
if (isFunctionLike(decl)) {
|
|
45
45
|
return decl;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// VARIABLE → FUNCTION
|
|
48
|
+
// VARIABLE → FUNCTION const fn = () => {}
|
|
49
49
|
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
50
50
|
if (isFunctionLike(decl.initializer)) {
|
|
51
51
|
return decl.initializer;
|
|
@@ -63,7 +63,12 @@ function resolveFromSymbol(
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// CLASS METHOD
|
|
67
|
+
if (ts.isMethodDeclaration(decl)) {
|
|
68
|
+
return decl;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// interface / type method
|
|
67
72
|
if (ts.isMethodSignature(decl)) {
|
|
68
73
|
const parent = decl.parent;
|
|
69
74
|
|
|
@@ -71,14 +76,10 @@ function resolveFromSymbol(
|
|
|
71
76
|
ts.isInterfaceDeclaration(parent) ||
|
|
72
77
|
ts.isTypeLiteralNode(parent)
|
|
73
78
|
) {
|
|
74
|
-
const name = decl.name.getText();
|
|
75
|
-
|
|
76
79
|
const type = checker.getTypeAtLocation(parent);
|
|
77
80
|
const props = type.getProperties();
|
|
78
81
|
|
|
79
82
|
for (const prop of props) {
|
|
80
|
-
if (prop.name !== name) continue;
|
|
81
|
-
|
|
82
83
|
const resolved = resolveFromSymbol(
|
|
83
84
|
prop,
|
|
84
85
|
checker,
|
|
@@ -91,10 +92,24 @@ function resolveFromSymbol(
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
// fallback recursion
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
95
|
+
const inner = checker.getSymbolAtLocation(decl);
|
|
96
|
+
if (inner && inner !== symbol) {
|
|
96
97
|
const resolved = resolveFromSymbol(
|
|
97
|
-
|
|
98
|
+
inner,
|
|
99
|
+
checker,
|
|
100
|
+
visited,
|
|
101
|
+
depth + 1
|
|
102
|
+
);
|
|
103
|
+
if (resolved) return resolved;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Exports (re-export)
|
|
108
|
+
const exports = (symbol as any).exports;
|
|
109
|
+
if (exports) {
|
|
110
|
+
for (const exp of exports.values()) {
|
|
111
|
+
const resolved = resolveFromSymbol(
|
|
112
|
+
exp,
|
|
98
113
|
checker,
|
|
99
114
|
visited,
|
|
100
115
|
depth + 1
|
|
@@ -106,17 +121,19 @@ function resolveFromSymbol(
|
|
|
106
121
|
return null;
|
|
107
122
|
}
|
|
108
123
|
|
|
124
|
+
// MAIN FUNCTION
|
|
109
125
|
export function resolveFunctionFromCall(
|
|
110
126
|
expr: ts.Expression,
|
|
111
127
|
checker: ts.TypeChecker
|
|
112
128
|
): ts.FunctionLikeDeclaration | null {
|
|
113
|
-
|
|
114
|
-
// tracker.track
|
|
129
|
+
// obj.method()
|
|
115
130
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
116
131
|
let symbol = checker.getSymbolAtLocation(expr.name);
|
|
117
132
|
|
|
118
133
|
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
119
|
-
|
|
134
|
+
try {
|
|
135
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
136
|
+
} catch {}
|
|
120
137
|
}
|
|
121
138
|
|
|
122
139
|
if (symbol) {
|
|
@@ -125,7 +142,7 @@ export function resolveFunctionFromCall(
|
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
//
|
|
145
|
+
// obj["method"]()
|
|
129
146
|
if (ts.isElementAccessExpression(expr)) {
|
|
130
147
|
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
131
148
|
|
|
@@ -135,7 +152,7 @@ export function resolveFunctionFromCall(
|
|
|
135
152
|
}
|
|
136
153
|
}
|
|
137
154
|
|
|
138
|
-
//
|
|
155
|
+
// Call signatures
|
|
139
156
|
const type = checker.getTypeAtLocation(expr);
|
|
140
157
|
const signatures = type.getCallSignatures();
|
|
141
158
|
|
|
@@ -157,7 +174,23 @@ export function resolveFunctionFromCall(
|
|
|
157
174
|
}
|
|
158
175
|
}
|
|
159
176
|
|
|
160
|
-
// fallback
|
|
177
|
+
// Identifier fallback
|
|
178
|
+
if (ts.isIdentifier(expr)) {
|
|
179
|
+
let symbol = checker.getSymbolAtLocation(expr);
|
|
180
|
+
|
|
181
|
+
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
182
|
+
try {
|
|
183
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
184
|
+
} catch {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (symbol) {
|
|
188
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
189
|
+
if (resolved) return resolved;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// LAST fallback
|
|
161
194
|
const symbol = checker.getSymbolAtLocation(expr);
|
|
162
195
|
if (symbol) {
|
|
163
196
|
return resolveFromSymbol(symbol, checker);
|
|
@@ -7,9 +7,10 @@ export class TSService {
|
|
|
7
7
|
private fileNames = new Set<string>();
|
|
8
8
|
private service: ts.LanguageService;
|
|
9
9
|
|
|
10
|
-
private compilerOptions: ts.CompilerOptions;
|
|
11
|
-
private baseUrl: string;
|
|
12
|
-
private paths: Record<string, string[]>;
|
|
10
|
+
private readonly compilerOptions: ts.CompilerOptions;
|
|
11
|
+
private readonly baseUrl: string;
|
|
12
|
+
private readonly paths: Record<string, string[]>;
|
|
13
|
+
|
|
13
14
|
private usage = new Map<string, number>();
|
|
14
15
|
private tick = 0;
|
|
15
16
|
private MAX_FILES = 2000;
|
|
@@ -23,6 +24,7 @@ export class TSService {
|
|
|
23
24
|
|
|
24
25
|
let compilerOptions: ts.CompilerOptions = {
|
|
25
26
|
allowJs: true,
|
|
27
|
+
allowNonTsExtensions: true,
|
|
26
28
|
jsx: ts.JsxEmit.React,
|
|
27
29
|
jsxImportSource: "react",
|
|
28
30
|
moduleResolution: ts.ModuleResolutionKind.Node16,
|
|
@@ -60,7 +62,17 @@ export class TSService {
|
|
|
60
62
|
this.paths = paths;
|
|
61
63
|
|
|
62
64
|
const host: ts.LanguageServiceHost = {
|
|
63
|
-
getScriptFileNames: () =>
|
|
65
|
+
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
|
+
return Array.from(this.fileNames);
|
|
75
|
+
},
|
|
64
76
|
|
|
65
77
|
getScriptVersion: (file) => {
|
|
66
78
|
file = this.normalize(file);
|
|
@@ -69,13 +81,29 @@ export class TSService {
|
|
|
69
81
|
|
|
70
82
|
getScriptSnapshot: (file) => {
|
|
71
83
|
file = this.normalize(file);
|
|
72
|
-
|
|
73
84
|
const f = this.files.get(file);
|
|
74
85
|
if (f) {
|
|
75
86
|
this.touch(file);
|
|
76
87
|
return ts.ScriptSnapshot.fromString(f.content);
|
|
77
88
|
}
|
|
89
|
+
if (file.endsWith(".tsx")) {
|
|
90
|
+
const original = file.replace(/\.tsx$/, "");
|
|
91
|
+
|
|
92
|
+
if (fs.existsSync(original)) {
|
|
93
|
+
const content = fs.readFileSync(original, "utf-8");
|
|
94
|
+
|
|
95
|
+
this.files.set(file, {
|
|
96
|
+
content,
|
|
97
|
+
version: 1,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.fileNames.add(file);
|
|
101
|
+
this.touch(file);
|
|
102
|
+
this.evictIfNeeded();
|
|
78
103
|
|
|
104
|
+
return ts.ScriptSnapshot.fromString(content);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
79
107
|
try {
|
|
80
108
|
if (fs.existsSync(file)) {
|
|
81
109
|
const content = fs.readFileSync(file, "utf-8");
|
|
@@ -93,16 +121,22 @@ export class TSService {
|
|
|
93
121
|
}
|
|
94
122
|
} catch {}
|
|
95
123
|
|
|
96
|
-
// fallback
|
|
97
124
|
return ts.ScriptSnapshot.fromString("");
|
|
98
125
|
},
|
|
99
|
-
|
|
100
126
|
getCurrentDirectory: () => this.root,
|
|
101
127
|
getCompilationSettings: () => this.compilerOptions,
|
|
102
128
|
getDefaultLibFileName: (opts) =>
|
|
103
129
|
ts.getDefaultLibFilePath(opts),
|
|
104
|
-
fileExists:
|
|
105
|
-
|
|
130
|
+
fileExists: (file) => {
|
|
131
|
+
file = this.normalize(file);
|
|
132
|
+
return this.files.has(file) || ts.sys.fileExists(file);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
readFile: (file) => {
|
|
136
|
+
file = this.normalize(file);
|
|
137
|
+
return this.files.get(file)?.content ?? ts.sys.readFile(file);
|
|
138
|
+
},
|
|
139
|
+
|
|
106
140
|
readDirectory: ts.sys.readDirectory,
|
|
107
141
|
};
|
|
108
142
|
|
|
@@ -122,7 +156,7 @@ export class TSService {
|
|
|
122
156
|
private evictIfNeeded() {
|
|
123
157
|
if (this.fileNames.size <= this.MAX_FILES) return;
|
|
124
158
|
|
|
125
|
-
let
|
|
159
|
+
let oldest: string | null = null;
|
|
126
160
|
let oldestTick = Infinity;
|
|
127
161
|
|
|
128
162
|
for (const f of this.fileNames) {
|
|
@@ -130,20 +164,20 @@ export class TSService {
|
|
|
130
164
|
|
|
131
165
|
if (t < oldestTick) {
|
|
132
166
|
oldestTick = t;
|
|
133
|
-
|
|
167
|
+
oldest = f;
|
|
134
168
|
}
|
|
135
169
|
}
|
|
136
170
|
|
|
137
|
-
if (
|
|
138
|
-
this.files.delete(
|
|
139
|
-
this.fileNames.delete(
|
|
140
|
-
this.usage.delete(
|
|
171
|
+
if (oldest) {
|
|
172
|
+
this.files.delete(oldest);
|
|
173
|
+
this.fileNames.delete(oldest);
|
|
174
|
+
this.usage.delete(oldest);
|
|
141
175
|
}
|
|
142
176
|
}
|
|
143
177
|
|
|
144
178
|
private ensureProgram() {
|
|
145
179
|
try {
|
|
146
|
-
this.service.getProgram();
|
|
180
|
+
this.service.getProgram()?.getTypeChecker();
|
|
147
181
|
} catch {}
|
|
148
182
|
}
|
|
149
183
|
|