@eventra_dev/eventra-cli 0.2.4 → 0.3.2
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 +143 -32
- package/dist/analysis/cache/evaluationCache.js +21 -0
- package/dist/analysis/cache/resolvedCallCache.js +21 -0
- package/dist/analysis/cache/resolvedExportCache.js +21 -0
- package/dist/analysis/cache/semanticDependencyGraph.js +25 -0
- package/dist/analysis/cache/symbolInvalidation.js +20 -0
- package/dist/analysis/extractor/extractor.js +47 -0
- package/dist/analysis/extractor/propagationExtractor.js +52 -0
- package/dist/analysis/pipeline/semanticPass.js +2 -0
- package/dist/analysis/resolver/callResolver.js +125 -0
- package/dist/analysis/resolver/exportResolver.js +75 -0
- package/dist/analysis/resolver/resolver.js +133 -0
- package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +193 -0
- package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +105 -0
- package/dist/analysis/scanner/scanner.js +117 -0
- package/dist/analysis/scanner/sinkDetector.js +91 -0
- package/dist/analysis/shared/evaluationContext.js +9 -0
- package/dist/analysis/shared/propagation.js +3 -0
- package/dist/analysis/shared/returnPropagation.js +2 -0
- package/dist/analysis/shared/types.js +2 -0
- package/dist/analysis/shared/utils.js +62 -0
- package/dist/analysis/symbols/symbolUtils.js +68 -0
- package/dist/analysis/symbols/wrapperRegistry.js +55 -0
- package/dist/cli/check.js +73 -0
- package/dist/{commands → cli}/init.js +1 -1
- package/dist/{commands → cli}/send.js +1 -1
- package/dist/cli/sync.js +56 -0
- package/dist/{commands → cli}/watch.js +41 -25
- package/dist/compiler/compilerContext.js +204 -0
- package/dist/compiler/documentRegistry.js +78 -0
- package/dist/compiler/importGraph.js +83 -0
- package/dist/compiler/scheduler.js +29 -0
- package/dist/core/EventraEngine.js +234 -0
- package/dist/core/projectScanner.js +73 -0
- package/dist/{utils → filesystem}/getVirtualFile.js +1 -3
- package/dist/filesystem/processFile.js +38 -0
- package/dist/index.js +5 -5
- package/dist/shared/buildSyntheticTsx.js +20 -0
- package/dist/shared/templateExpressionExtractor.js +22 -0
- package/package.json +32 -9
- package/.github/workflows/release.yml +0 -43
- package/ARCHITECTURE.md +0 -88
- package/assets/eventra-icon-animated.svg +0 -19
- package/dist/commands/check.js +0 -78
- package/dist/commands/sync.js +0 -79
- package/dist/engine/boundary.js +0 -14
- package/dist/engine/callResolver.js +0 -58
- package/dist/engine/engine.js +0 -154
- package/dist/engine/exportResolver.js +0 -46
- package/dist/engine/functionAnalyzer.js +0 -71
- package/dist/engine/graph.js +0 -168
- package/dist/engine/languageService.js +0 -152
- package/dist/engine/resolver.js +0 -149
- package/dist/engine/scanner.js +0 -230
- package/dist/utils/processFile.js +0 -28
- package/dist/utils/universal.js +0 -190
- package/src/commands/check.ts +0 -91
- package/src/commands/init.ts +0 -174
- package/src/commands/send.ts +0 -213
- package/src/commands/sync.ts +0 -106
- package/src/commands/watch.ts +0 -144
- package/src/engine/boundary.ts +0 -12
- package/src/engine/callResolver.ts +0 -65
- package/src/engine/engine.ts +0 -227
- package/src/engine/exportResolver.ts +0 -56
- package/src/engine/functionAnalyzer.ts +0 -81
- package/src/engine/graph.ts +0 -193
- package/src/engine/hash.ts +0 -5
- package/src/engine/languageService.ts +0 -197
- package/src/engine/resolver.ts +0 -177
- package/src/engine/scanner.ts +0 -298
- package/src/index.ts +0 -49
- package/src/types.ts +0 -35
- package/src/utils/config.ts +0 -53
- package/src/utils/getVirtualFile.ts +0 -5
- package/src/utils/processFile.ts +0 -29
- package/src/utils/universal.ts +0 -249
- package/tests/fixtures/backend/express/app.ts +0 -233
- package/tests/fixtures/backend/nest/service.ts +0 -70
- package/tests/fixtures/backend/node/index.ts +0 -63
- package/tests/fixtures/frontend/next/page.tsx +0 -101
- package/tests/fixtures/frontend/react/App.tsx +0 -104
- package/tests/fixtures/frontend/vue/App.vue +0 -83
- package/tests/fixtures/wrappers/component/test.tsx +0 -62
- package/tests/fixtures/wrappers/function/test.ts +0 -60
- package/tests/run.ts +0 -106
- package/tsconfig.json +0 -14
- /package/dist/{utils → config}/config.js +0 -0
- /package/dist/{engine → shared}/hash.js +0 -0
package/dist/engine/engine.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.EventraEngine = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const languageService_1 = require("./languageService");
|
|
10
|
-
const scanner_1 = require("./scanner");
|
|
11
|
-
const graph_1 = require("./graph");
|
|
12
|
-
const hash_1 = require("./hash");
|
|
13
|
-
const boundary_1 = require("./boundary");
|
|
14
|
-
class EventraEngine {
|
|
15
|
-
ts;
|
|
16
|
-
graph;
|
|
17
|
-
wrapperCache = new Map();
|
|
18
|
-
fileResults = new Map();
|
|
19
|
-
fileHash = new Map();
|
|
20
|
-
constructor(root) {
|
|
21
|
-
this.ts = new languageService_1.TSService(root);
|
|
22
|
-
const { baseUrl, paths } = this.ts.getModuleResolutionConfig();
|
|
23
|
-
this.graph = new graph_1.DependencyGraph(baseUrl, paths);
|
|
24
|
-
}
|
|
25
|
-
normalize(file) {
|
|
26
|
-
return path_1.default.resolve(file).replace(/\\/g, "/");
|
|
27
|
-
}
|
|
28
|
-
empty() {
|
|
29
|
-
return {
|
|
30
|
-
events: new Set(),
|
|
31
|
-
detectedFunctionWrappers: new Set(),
|
|
32
|
-
detectedComponentWrappers: new Map(),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
// PRELOAD
|
|
36
|
-
preloadFile(file, content) {
|
|
37
|
-
file = this.normalize(file);
|
|
38
|
-
this.ts.updateFile(file, content);
|
|
39
|
-
const source = this.ts.getSourceFile(file);
|
|
40
|
-
if (source) {
|
|
41
|
-
this.graph.update(file, source);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
// SCAN FILE (cached)
|
|
45
|
-
scanFile(file, content, config) {
|
|
46
|
-
file = this.normalize(file);
|
|
47
|
-
const h = (0, hash_1.hash)(content);
|
|
48
|
-
if (this.fileHash.get(file) === h) {
|
|
49
|
-
return this.fileResults.get(file) ?? this.empty();
|
|
50
|
-
}
|
|
51
|
-
this.fileHash.set(file, h);
|
|
52
|
-
this.ts.updateFile(file, content);
|
|
53
|
-
const source = this.ts.getSourceFile(file);
|
|
54
|
-
const checker = this.ts.getChecker();
|
|
55
|
-
if (!source || !checker) {
|
|
56
|
-
return this.empty();
|
|
57
|
-
}
|
|
58
|
-
this.graph.update(file, source);
|
|
59
|
-
const res = (0, scanner_1.scanSource)(source, checker, config, new Set(), undefined, this.wrapperCache);
|
|
60
|
-
this.fileResults.set(file, res);
|
|
61
|
-
return res;
|
|
62
|
-
}
|
|
63
|
-
// UPDATE FILE (incremental)
|
|
64
|
-
updateFile(file, content, config) {
|
|
65
|
-
file = this.normalize(file);
|
|
66
|
-
// EARLY EXIT
|
|
67
|
-
if (this.fileHash.get(file) === (0, hash_1.hash)(content)) {
|
|
68
|
-
return this.fileResults.get(file) ?? this.empty();
|
|
69
|
-
}
|
|
70
|
-
// invalidate wrapper cache
|
|
71
|
-
this.wrapperCache.clear();
|
|
72
|
-
const res = this.scanFile(file, content, config);
|
|
73
|
-
const dependents = this.graph.getAllDependentsDeep(file);
|
|
74
|
-
for (const dep of dependents) {
|
|
75
|
-
const abs = this.normalize(dep);
|
|
76
|
-
if (abs === file)
|
|
77
|
-
continue; // self-skip
|
|
78
|
-
if ((0, boundary_1.isExternalFile)(abs))
|
|
79
|
-
continue;
|
|
80
|
-
let depContent = this.ts.getFileContent(abs);
|
|
81
|
-
if (!depContent) {
|
|
82
|
-
try {
|
|
83
|
-
depContent = fs_1.default.readFileSync(abs, "utf-8");
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const h = (0, hash_1.hash)(depContent);
|
|
90
|
-
if (this.fileHash.get(abs) === h) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
this.fileHash.set(abs, h);
|
|
94
|
-
this.ts.updateFile(abs, depContent);
|
|
95
|
-
const source = this.ts.getSourceFile(abs);
|
|
96
|
-
const checker = this.ts.getChecker();
|
|
97
|
-
if (!source || !checker)
|
|
98
|
-
continue;
|
|
99
|
-
this.graph.update(abs, source);
|
|
100
|
-
const r = (0, scanner_1.scanSource)(source, checker, config, new Set(), undefined, this.wrapperCache);
|
|
101
|
-
this.fileResults.set(abs, r);
|
|
102
|
-
}
|
|
103
|
-
return res;
|
|
104
|
-
}
|
|
105
|
-
// GET ALL EVENTS
|
|
106
|
-
getAllEvents() {
|
|
107
|
-
const all = new Set();
|
|
108
|
-
for (const r of this.fileResults.values()) {
|
|
109
|
-
r.events.forEach(e => all.add(e));
|
|
110
|
-
}
|
|
111
|
-
return [...all];
|
|
112
|
-
}
|
|
113
|
-
// REMOVE FILE
|
|
114
|
-
removeFile(file, config) {
|
|
115
|
-
file = this.normalize(file);
|
|
116
|
-
this.wrapperCache.clear();
|
|
117
|
-
const dependents = this.graph.getAllDependentsDeep(file);
|
|
118
|
-
this.graph.remove(file);
|
|
119
|
-
this.ts.removeFile(file);
|
|
120
|
-
this.fileResults.delete(file);
|
|
121
|
-
this.fileHash.delete(file);
|
|
122
|
-
for (const dep of dependents) {
|
|
123
|
-
const abs = this.normalize(dep);
|
|
124
|
-
if (abs === file)
|
|
125
|
-
continue;
|
|
126
|
-
if ((0, boundary_1.isExternalFile)(abs))
|
|
127
|
-
continue;
|
|
128
|
-
let content = this.ts.getFileContent(abs);
|
|
129
|
-
if (!content) {
|
|
130
|
-
try {
|
|
131
|
-
content = fs_1.default.readFileSync(abs, "utf-8");
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
this.fileHash.delete(abs);
|
|
138
|
-
this.ts.updateFile(abs, content);
|
|
139
|
-
const source = this.ts.getSourceFile(abs);
|
|
140
|
-
const checker = this.ts.getChecker();
|
|
141
|
-
if (!source || !checker)
|
|
142
|
-
continue;
|
|
143
|
-
this.graph.update(abs, source);
|
|
144
|
-
const r = (0, scanner_1.scanSource)(source, checker, config ?? {
|
|
145
|
-
events: [],
|
|
146
|
-
wrappers: [],
|
|
147
|
-
functionWrappers: [],
|
|
148
|
-
sync: { include: [], exclude: [] },
|
|
149
|
-
}, new Set(), undefined, this.wrapperCache);
|
|
150
|
-
this.fileResults.set(abs, r);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
exports.EventraEngine = EventraEngine;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveExportedSymbol = resolveExportedSymbol;
|
|
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
|
-
}
|
|
13
|
-
function resolveExportedSymbol(symbol, checker, visited = new Set()) {
|
|
14
|
-
if (visited.has(symbol))
|
|
15
|
-
return null;
|
|
16
|
-
visited.add(symbol);
|
|
17
|
-
// unwrap alias
|
|
18
|
-
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
19
|
-
const aliased = checker.getAliasedSymbol(symbol);
|
|
20
|
-
return resolveExportedSymbol(aliased, checker, visited);
|
|
21
|
-
}
|
|
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)) {
|
|
29
|
-
return symbol;
|
|
30
|
-
}
|
|
31
|
-
// exports (re-export)
|
|
32
|
-
for (const decl of decls) {
|
|
33
|
-
const source = decl.getSourceFile();
|
|
34
|
-
const moduleSymbol = source.symbol;
|
|
35
|
-
if (!moduleSymbol)
|
|
36
|
-
continue;
|
|
37
|
-
const exports = checker.getExportsOfModule(moduleSymbol);
|
|
38
|
-
for (const exp of exports) {
|
|
39
|
-
const resolved = resolveExportedSymbol(exp, checker, visited);
|
|
40
|
-
if (resolved?.getDeclarations()?.length) {
|
|
41
|
-
return resolved;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return symbol;
|
|
46
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.findTrackParamIndex = findTrackParamIndex;
|
|
7
|
-
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
-
function findTrackParamIndex(fn) {
|
|
9
|
-
const paramIndex = new Map();
|
|
10
|
-
fn.parameters.forEach((p, i) => {
|
|
11
|
-
if (typescript_1.default.isIdentifier(p.name)) {
|
|
12
|
-
paramIndex.set(p.name.text, i);
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
let result = null;
|
|
16
|
-
function check(arg) {
|
|
17
|
-
// identifier
|
|
18
|
-
if (typescript_1.default.isIdentifier(arg)) {
|
|
19
|
-
const idx = paramIndex.get(arg.text);
|
|
20
|
-
if (idx !== undefined) {
|
|
21
|
-
result = idx;
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
// template
|
|
26
|
-
if (typescript_1.default.isTemplateExpression(arg)) {
|
|
27
|
-
for (const span of arg.templateSpans) {
|
|
28
|
-
if (typescript_1.default.isIdentifier(span.expression)) {
|
|
29
|
-
const idx = paramIndex.get(span.expression.text);
|
|
30
|
-
if (idx !== undefined) {
|
|
31
|
-
result = idx;
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
// object { event: name }
|
|
38
|
-
if (typescript_1.default.isObjectLiteralExpression(arg)) {
|
|
39
|
-
for (const prop of arg.properties) {
|
|
40
|
-
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
41
|
-
continue;
|
|
42
|
-
const key = prop.name.getText();
|
|
43
|
-
if (["event", "name", "type"].includes(key)) {
|
|
44
|
-
return check(prop.initializer);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
function visit(node) {
|
|
51
|
-
if (result !== null)
|
|
52
|
-
return;
|
|
53
|
-
if (typescript_1.default.isCallExpression(node)) {
|
|
54
|
-
const expr = node.expression;
|
|
55
|
-
const isTrack = (typescript_1.default.isIdentifier(expr) && expr.text === "track") ||
|
|
56
|
-
(typescript_1.default.isPropertyAccessExpression(expr) &&
|
|
57
|
-
expr.name.text === "track");
|
|
58
|
-
if (!isTrack)
|
|
59
|
-
return;
|
|
60
|
-
for (const arg of node.arguments) {
|
|
61
|
-
if (check(arg))
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
typescript_1.default.forEachChild(node, visit);
|
|
66
|
-
}
|
|
67
|
-
if (fn.body) {
|
|
68
|
-
visit(fn.body);
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
}
|
package/dist/engine/graph.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DependencyGraph = void 0;
|
|
7
|
-
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
class DependencyGraph {
|
|
11
|
-
baseUrl;
|
|
12
|
-
paths;
|
|
13
|
-
reverse = new Map();
|
|
14
|
-
exports = new Map();
|
|
15
|
-
fileCache = new Map();
|
|
16
|
-
constructor(baseUrl, paths) {
|
|
17
|
-
this.baseUrl = baseUrl;
|
|
18
|
-
this.paths = paths;
|
|
19
|
-
}
|
|
20
|
-
normalize(file) {
|
|
21
|
-
return path_1.default.resolve(file).replace(/\\/g, "/");
|
|
22
|
-
}
|
|
23
|
-
// MODULE RESOLVE
|
|
24
|
-
resolveModule(module, from) {
|
|
25
|
-
// relative import
|
|
26
|
-
if (module.startsWith(".")) {
|
|
27
|
-
const abs = path_1.default.resolve(path_1.default.dirname(from), module);
|
|
28
|
-
return this.resolveFile(abs);
|
|
29
|
-
}
|
|
30
|
-
// tsconfig paths
|
|
31
|
-
for (const key in this.paths) {
|
|
32
|
-
const pattern = key.replace(/\*/g, "(.*)");
|
|
33
|
-
const regex = new RegExp("^" + pattern + "$");
|
|
34
|
-
const match = module.match(regex);
|
|
35
|
-
if (!match)
|
|
36
|
-
continue;
|
|
37
|
-
const wildcard = match.slice(1);
|
|
38
|
-
for (const target of this.paths[key]) {
|
|
39
|
-
let replaced = target;
|
|
40
|
-
wildcard.forEach(w => {
|
|
41
|
-
replaced = replaced.replace("*", w);
|
|
42
|
-
});
|
|
43
|
-
const abs = path_1.default.resolve(this.baseUrl, replaced);
|
|
44
|
-
const resolved = this.resolveFile(abs);
|
|
45
|
-
if (resolved)
|
|
46
|
-
return resolved;
|
|
47
|
-
}
|
|
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
|
|
58
|
-
const abs = path_1.default.resolve(this.baseUrl, module);
|
|
59
|
-
return this.resolveFile(abs);
|
|
60
|
-
}
|
|
61
|
-
// FILE RESOLVE
|
|
62
|
-
resolveFile(base) {
|
|
63
|
-
if (this.fileCache.has(base)) {
|
|
64
|
-
return this.fileCache.get(base);
|
|
65
|
-
}
|
|
66
|
-
const tryFiles = [
|
|
67
|
-
base,
|
|
68
|
-
base + ".ts",
|
|
69
|
-
base + ".tsx",
|
|
70
|
-
base + ".js",
|
|
71
|
-
base + ".jsx",
|
|
72
|
-
base + ".mjs",
|
|
73
|
-
base + ".cjs",
|
|
74
|
-
base + ".d.ts",
|
|
75
|
-
base + ".vue",
|
|
76
|
-
base + ".svelte",
|
|
77
|
-
base + ".astro",
|
|
78
|
-
path_1.default.join(base, "index.ts"),
|
|
79
|
-
path_1.default.join(base, "index.tsx"),
|
|
80
|
-
path_1.default.join(base, "index.js"),
|
|
81
|
-
];
|
|
82
|
-
for (const f of tryFiles) {
|
|
83
|
-
if (fs_1.default.existsSync(f)) {
|
|
84
|
-
const normalized = this.normalize(f);
|
|
85
|
-
this.fileCache.set(base, normalized);
|
|
86
|
-
return normalized;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
this.fileCache.set(base, null);
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
// UPDATE GRAPH
|
|
93
|
-
update(file, source) {
|
|
94
|
-
const normalizedFile = this.normalize(file);
|
|
95
|
-
for (const deps of this.reverse.values()) {
|
|
96
|
-
deps.delete(normalizedFile);
|
|
97
|
-
}
|
|
98
|
-
this.exports.set(normalizedFile, new Set());
|
|
99
|
-
for (const stmt of source.statements) {
|
|
100
|
-
// IMPORT
|
|
101
|
-
if (typescript_1.default.isImportDeclaration(stmt)) {
|
|
102
|
-
// ignore import type
|
|
103
|
-
if (stmt.importClause?.importKind === "type")
|
|
104
|
-
continue;
|
|
105
|
-
const module = stmt.moduleSpecifier
|
|
106
|
-
.getText()
|
|
107
|
-
.replace(/['"]/g, "");
|
|
108
|
-
const resolved = this.resolveModule(module, normalizedFile);
|
|
109
|
-
if (!resolved)
|
|
110
|
-
continue;
|
|
111
|
-
if (!this.reverse.has(resolved)) {
|
|
112
|
-
this.reverse.set(resolved, new Set());
|
|
113
|
-
}
|
|
114
|
-
this.reverse.get(resolved).add(normalizedFile);
|
|
115
|
-
}
|
|
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, "");
|
|
123
|
-
const resolved = this.resolveModule(module, normalizedFile);
|
|
124
|
-
if (!resolved)
|
|
125
|
-
continue;
|
|
126
|
-
this.exports.get(normalizedFile)?.add(resolved);
|
|
127
|
-
if (!this.reverse.has(resolved)) {
|
|
128
|
-
this.reverse.set(resolved, new Set());
|
|
129
|
-
}
|
|
130
|
-
this.reverse.get(resolved).add(normalizedFile);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// DEPENDENTS
|
|
135
|
-
getAllDependentsDeep(file) {
|
|
136
|
-
const start = this.normalize(file);
|
|
137
|
-
const visited = new Set();
|
|
138
|
-
const result = new Set();
|
|
139
|
-
const walk = (f) => {
|
|
140
|
-
if (visited.has(f))
|
|
141
|
-
return;
|
|
142
|
-
visited.add(f);
|
|
143
|
-
const deps = this.reverse.get(f);
|
|
144
|
-
deps?.forEach(d => {
|
|
145
|
-
result.add(d);
|
|
146
|
-
walk(d);
|
|
147
|
-
});
|
|
148
|
-
const ex = this.exports.get(f);
|
|
149
|
-
ex?.forEach(e => walk(e));
|
|
150
|
-
};
|
|
151
|
-
walk(start);
|
|
152
|
-
return [...result];
|
|
153
|
-
}
|
|
154
|
-
// REMOVE
|
|
155
|
-
remove(file) {
|
|
156
|
-
const normalized = this.normalize(file);
|
|
157
|
-
this.reverse.delete(normalized);
|
|
158
|
-
this.exports.delete(normalized);
|
|
159
|
-
this.fileCache.delete(normalized);
|
|
160
|
-
for (const set of this.reverse.values()) {
|
|
161
|
-
set.delete(normalized);
|
|
162
|
-
}
|
|
163
|
-
for (const set of this.exports.values()) {
|
|
164
|
-
set.delete(normalized);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
exports.DependencyGraph = DependencyGraph;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.TSService = void 0;
|
|
7
|
-
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
class TSService {
|
|
11
|
-
root;
|
|
12
|
-
files = new Map();
|
|
13
|
-
fileNames = new Set();
|
|
14
|
-
service;
|
|
15
|
-
compilerOptions;
|
|
16
|
-
baseUrl;
|
|
17
|
-
paths;
|
|
18
|
-
constructor(root) {
|
|
19
|
-
this.root = root;
|
|
20
|
-
const configPath = typescript_1.default.findConfigFile(root, typescript_1.default.sys.fileExists, "tsconfig.json");
|
|
21
|
-
let compilerOptions = {
|
|
22
|
-
allowJs: true,
|
|
23
|
-
allowNonTsExtensions: true,
|
|
24
|
-
jsx: typescript_1.default.JsxEmit.Preserve,
|
|
25
|
-
jsxImportSource: "react",
|
|
26
|
-
moduleResolution: typescript_1.default.ModuleResolutionKind.Node16,
|
|
27
|
-
target: typescript_1.default.ScriptTarget.ESNext,
|
|
28
|
-
esModuleInterop: true,
|
|
29
|
-
allowSyntheticDefaultImports: true,
|
|
30
|
-
skipLibCheck: true,
|
|
31
|
-
};
|
|
32
|
-
let baseUrl = root;
|
|
33
|
-
let paths = {};
|
|
34
|
-
if (configPath) {
|
|
35
|
-
const configFile = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
|
|
36
|
-
const parsed = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, root);
|
|
37
|
-
compilerOptions = {
|
|
38
|
-
...compilerOptions,
|
|
39
|
-
...parsed.options,
|
|
40
|
-
};
|
|
41
|
-
paths = parsed.options.paths || {};
|
|
42
|
-
baseUrl = parsed.options.baseUrl
|
|
43
|
-
? path_1.default.resolve(root, parsed.options.baseUrl)
|
|
44
|
-
: root;
|
|
45
|
-
}
|
|
46
|
-
this.compilerOptions = compilerOptions;
|
|
47
|
-
this.baseUrl = baseUrl;
|
|
48
|
-
this.paths = paths;
|
|
49
|
-
const allFiles = typescript_1.default.sys.readDirectory(root, [".ts", ".tsx", ".js", ".jsx"], undefined, undefined);
|
|
50
|
-
for (const f of allFiles) {
|
|
51
|
-
this.fileNames.add(this.normalize(f));
|
|
52
|
-
}
|
|
53
|
-
const host = {
|
|
54
|
-
getScriptFileNames: () => {
|
|
55
|
-
return Array.from(this.fileNames);
|
|
56
|
-
},
|
|
57
|
-
getScriptVersion: (file) => {
|
|
58
|
-
file = this.normalize(file);
|
|
59
|
-
return this.files.get(file)?.version.toString() ?? "0";
|
|
60
|
-
},
|
|
61
|
-
getScriptKind: (fileName) => {
|
|
62
|
-
fileName = this.normalize(fileName);
|
|
63
|
-
if (fileName.endsWith(".tsx")) {
|
|
64
|
-
return typescript_1.default.ScriptKind.TSX;
|
|
65
|
-
}
|
|
66
|
-
if (fileName.endsWith(".ts")) {
|
|
67
|
-
return typescript_1.default.ScriptKind.TS;
|
|
68
|
-
}
|
|
69
|
-
if (fileName.endsWith(".jsx")) {
|
|
70
|
-
return typescript_1.default.ScriptKind.JSX;
|
|
71
|
-
}
|
|
72
|
-
if (fileName.endsWith(".js")) {
|
|
73
|
-
return typescript_1.default.ScriptKind.JS;
|
|
74
|
-
}
|
|
75
|
-
return typescript_1.default.ScriptKind.Unknown;
|
|
76
|
-
},
|
|
77
|
-
getScriptSnapshot: (file) => {
|
|
78
|
-
file = this.normalize(file);
|
|
79
|
-
const f = this.files.get(file);
|
|
80
|
-
if (f) {
|
|
81
|
-
return typescript_1.default.ScriptSnapshot.fromString(f.content);
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
if (fs_1.default.existsSync(file)) {
|
|
85
|
-
const content = fs_1.default.readFileSync(file, "utf-8");
|
|
86
|
-
this.files.set(file, {
|
|
87
|
-
content,
|
|
88
|
-
version: 1,
|
|
89
|
-
});
|
|
90
|
-
this.fileNames.add(file);
|
|
91
|
-
return typescript_1.default.ScriptSnapshot.fromString(content);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
}
|
|
96
|
-
return typescript_1.default.ScriptSnapshot.fromString("");
|
|
97
|
-
},
|
|
98
|
-
getCurrentDirectory: () => this.root,
|
|
99
|
-
getCompilationSettings: () => this.compilerOptions,
|
|
100
|
-
getDefaultLibFileName: (opts) => typescript_1.default.getDefaultLibFilePath(opts),
|
|
101
|
-
fileExists: (file) => {
|
|
102
|
-
file = this.normalize(file);
|
|
103
|
-
return this.files.has(file) || typescript_1.default.sys.fileExists(file);
|
|
104
|
-
},
|
|
105
|
-
readFile: (file) => {
|
|
106
|
-
file = this.normalize(file);
|
|
107
|
-
return this.files.get(file)?.content ?? typescript_1.default.sys.readFile(file);
|
|
108
|
-
},
|
|
109
|
-
readDirectory: typescript_1.default.sys.readDirectory,
|
|
110
|
-
};
|
|
111
|
-
this.service = typescript_1.default.createLanguageService(host);
|
|
112
|
-
}
|
|
113
|
-
normalize(file) {
|
|
114
|
-
return path_1.default.resolve(file).replace(/\\/g, "/");
|
|
115
|
-
}
|
|
116
|
-
// PUBLIC API
|
|
117
|
-
updateFile(file, content) {
|
|
118
|
-
file = this.normalize(file);
|
|
119
|
-
const prev = this.files.get(file);
|
|
120
|
-
this.files.set(file, {
|
|
121
|
-
content,
|
|
122
|
-
version: prev ? prev.version + 1 : 1,
|
|
123
|
-
});
|
|
124
|
-
this.fileNames.add(file);
|
|
125
|
-
}
|
|
126
|
-
removeFile(file) {
|
|
127
|
-
file = this.normalize(file);
|
|
128
|
-
this.files.delete(file);
|
|
129
|
-
this.fileNames.delete(file);
|
|
130
|
-
}
|
|
131
|
-
getProgram() {
|
|
132
|
-
return this.service.getProgram();
|
|
133
|
-
}
|
|
134
|
-
getChecker() {
|
|
135
|
-
return this.getProgram()?.getTypeChecker();
|
|
136
|
-
}
|
|
137
|
-
getSourceFile(file) {
|
|
138
|
-
file = this.normalize(file);
|
|
139
|
-
return this.getProgram()?.getSourceFile(file);
|
|
140
|
-
}
|
|
141
|
-
getFileContent(file) {
|
|
142
|
-
file = this.normalize(file);
|
|
143
|
-
return this.files.get(file)?.content;
|
|
144
|
-
}
|
|
145
|
-
getModuleResolutionConfig() {
|
|
146
|
-
return {
|
|
147
|
-
baseUrl: this.baseUrl,
|
|
148
|
-
paths: this.paths,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
exports.TSService = TSService;
|