ts-knowledge-graph 0.1.2 → 0.1.6
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 +99 -41
- package/contribs/webview/README.md +83 -0
- package/contribs/webview/web/css/style.css +310 -0
- package/contribs/webview/web/index.html +109 -0
- package/contribs/webview/web/js/app.js +1249 -0
- package/contribs/webview/web/js_autogenerated/.gitignore +3 -0
- package/contribs/webview/web/js_autogenerated/kind_descriptions.js +39 -0
- package/contribs/webview/web/types/app_globals.d.ts +154 -0
- package/dist/benchmark/benchmark_stats.d.ts +41 -0
- package/dist/benchmark/benchmark_stats.d.ts.map +1 -0
- package/dist/benchmark/benchmark_stats.js +61 -0
- package/dist/benchmark/benchmark_stats.js.map +1 -0
- package/dist/benchmark/node_benchmark.d.ts +78 -0
- package/dist/benchmark/node_benchmark.d.ts.map +1 -0
- package/dist/benchmark/node_benchmark.js +112 -0
- package/dist/benchmark/node_benchmark.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/cluster/cluster_weights.d.ts +20 -0
- package/dist/cluster/cluster_weights.d.ts.map +1 -0
- package/dist/cluster/cluster_weights.js +32 -0
- package/dist/cluster/cluster_weights.js.map +1 -0
- package/dist/cluster/community_detector.d.ts +61 -0
- package/dist/cluster/community_detector.d.ts.map +1 -0
- package/dist/cluster/community_detector.js +120 -0
- package/dist/cluster/community_detector.js.map +1 -0
- package/dist/cluster/community_labeler.d.ts +84 -0
- package/dist/cluster/community_labeler.d.ts.map +1 -0
- package/dist/cluster/community_labeler.js +194 -0
- package/dist/cluster/community_labeler.js.map +1 -0
- package/dist/cluster/graph_clusterer.d.ts +47 -0
- package/dist/cluster/graph_clusterer.d.ts.map +1 -0
- package/dist/cluster/graph_clusterer.js +126 -0
- package/dist/cluster/graph_clusterer.js.map +1 -0
- package/dist/commands/benchmark_command.d.ts +11 -0
- package/dist/commands/benchmark_command.d.ts.map +1 -0
- package/dist/commands/benchmark_command.js +94 -0
- package/dist/commands/benchmark_command.js.map +1 -0
- package/dist/commands/blast_radius_command.d.ts.map +1 -1
- package/dist/commands/blast_radius_command.js +7 -6
- package/dist/commands/blast_radius_command.js.map +1 -1
- package/dist/commands/cluster_command.d.ts +7 -0
- package/dist/commands/cluster_command.d.ts.map +1 -0
- package/dist/commands/cluster_command.js +55 -0
- package/dist/commands/cluster_command.js.map +1 -0
- package/dist/commands/command_helpers.d.ts +9 -4
- package/dist/commands/command_helpers.d.ts.map +1 -1
- package/dist/commands/command_helpers.js +13 -8
- package/dist/commands/command_helpers.js.map +1 -1
- package/dist/commands/cost_command.d.ts +13 -0
- package/dist/commands/cost_command.d.ts.map +1 -0
- package/dist/commands/cost_command.js +139 -0
- package/dist/commands/cost_command.js.map +1 -0
- package/dist/commands/{load.d.ts → enrich_command.d.ts} +3 -2
- package/dist/commands/enrich_command.d.ts.map +1 -0
- package/dist/commands/enrich_command.js +64 -0
- package/dist/commands/enrich_command.js.map +1 -0
- package/dist/commands/extract_command.d.ts.map +1 -1
- package/dist/commands/extract_command.js +12 -6
- package/dist/commands/extract_command.js.map +1 -1
- package/dist/commands/hotspots_command.d.ts +7 -0
- package/dist/commands/hotspots_command.d.ts.map +1 -0
- package/dist/commands/hotspots_command.js +68 -0
- package/dist/commands/hotspots_command.js.map +1 -0
- package/dist/commands/install_command.d.ts +15 -6
- package/dist/commands/install_command.d.ts.map +1 -1
- package/dist/commands/install_command.js +62 -25
- package/dist/commands/install_command.js.map +1 -1
- package/dist/commands/load_command.d.ts.map +1 -1
- package/dist/commands/load_command.js +20 -13
- package/dist/commands/load_command.js.map +1 -1
- package/dist/commands/neighbors_command.d.ts.map +1 -1
- package/dist/commands/neighbors_command.js +6 -5
- package/dist/commands/neighbors_command.js.map +1 -1
- package/dist/commands/references_command.d.ts.map +1 -1
- package/dist/commands/references_command.js +6 -5
- package/dist/commands/references_command.js.map +1 -1
- package/dist/commands/report_command.d.ts +16 -0
- package/dist/commands/report_command.d.ts.map +1 -0
- package/dist/commands/report_command.js +115 -0
- package/dist/commands/report_command.js.map +1 -0
- package/dist/commands/verify_command.d.ts +8 -0
- package/dist/commands/verify_command.d.ts.map +1 -0
- package/dist/commands/verify_command.js +57 -0
- package/dist/commands/verify_command.js.map +1 -0
- package/dist/commands/web_command.d.ts +27 -0
- package/dist/commands/web_command.d.ts.map +1 -1
- package/dist/commands/web_command.js +109 -3
- package/dist/commands/web_command.js.map +1 -1
- package/dist/commands/webview_command.d.ts +36 -0
- package/dist/commands/webview_command.d.ts.map +1 -0
- package/dist/commands/webview_command.js +186 -0
- package/dist/commands/webview_command.js.map +1 -0
- package/dist/enrich/cpu_profile.d.ts +160 -0
- package/dist/enrich/cpu_profile.d.ts.map +1 -0
- package/dist/enrich/cpu_profile.js +185 -0
- package/dist/enrich/cpu_profile.js.map +1 -0
- package/dist/enrich/runtime_enricher.d.ts +64 -0
- package/dist/enrich/runtime_enricher.d.ts.map +1 -0
- package/dist/enrich/runtime_enricher.js +98 -0
- package/dist/enrich/runtime_enricher.js.map +1 -0
- package/dist/enrich/runtime_join.d.ts +124 -0
- package/dist/enrich/runtime_join.d.ts.map +1 -0
- package/dist/enrich/runtime_join.js +270 -0
- package/dist/enrich/runtime_join.js.map +1 -0
- package/dist/extract/api_extractor.d.ts +24 -0
- package/dist/extract/api_extractor.d.ts.map +1 -0
- package/dist/extract/api_extractor.js +71 -0
- package/dist/extract/api_extractor.js.map +1 -0
- package/dist/extract/config_extractor.d.ts +22 -0
- package/dist/extract/config_extractor.d.ts.map +1 -0
- package/dist/extract/config_extractor.js +61 -0
- package/dist/extract/config_extractor.js.map +1 -0
- package/dist/extract/endpoint_extractor.d.ts +36 -0
- package/dist/extract/endpoint_extractor.d.ts.map +1 -0
- package/dist/extract/endpoint_extractor.js +117 -0
- package/dist/extract/endpoint_extractor.js.map +1 -0
- package/dist/extract/git_source.d.ts +23 -0
- package/dist/extract/git_source.d.ts.map +1 -0
- package/dist/extract/git_source.js +75 -0
- package/dist/extract/git_source.js.map +1 -0
- package/dist/extract/graph_builder.d.ts +8 -0
- package/dist/extract/graph_builder.d.ts.map +1 -1
- package/dist/extract/graph_builder.js +23 -1
- package/dist/extract/graph_builder.js.map +1 -1
- package/dist/extract/node_id.d.ts +16 -0
- package/dist/extract/node_id.d.ts.map +1 -1
- package/dist/extract/node_id.js +22 -0
- package/dist/extract/node_id.js.map +1 -1
- package/dist/extract/scope_resolver.d.ts +22 -0
- package/dist/extract/scope_resolver.d.ts.map +1 -0
- package/dist/extract/scope_resolver.js +53 -0
- package/dist/extract/scope_resolver.js.map +1 -0
- package/dist/extract/semantic_extractor.d.ts +25 -0
- package/dist/extract/semantic_extractor.d.ts.map +1 -1
- package/dist/extract/semantic_extractor.js +96 -2
- package/dist/extract/semantic_extractor.js.map +1 -1
- package/dist/extract/structural_extractor.d.ts +6 -0
- package/dist/extract/structural_extractor.d.ts.map +1 -1
- package/dist/extract/structural_extractor.js +22 -12
- package/dist/extract/structural_extractor.js.map +1 -1
- package/dist/project_root.d.ts +7 -0
- package/dist/project_root.d.ts.map +1 -0
- package/dist/project_root.js +9 -0
- package/dist/project_root.js.map +1 -0
- package/dist/query/graph_query.d.ts +269 -0
- package/dist/query/graph_query.d.ts.map +1 -1
- package/dist/query/graph_query.js +585 -11
- package/dist/query/graph_query.js.map +1 -1
- package/dist/report/graph_report.d.ts +51 -0
- package/dist/report/graph_report.d.ts.map +1 -0
- package/dist/report/graph_report.js +312 -0
- package/dist/report/graph_report.js.map +1 -0
- package/dist/report/pdf_renderer.d.ts +22 -0
- package/dist/report/pdf_renderer.d.ts.map +1 -0
- package/dist/report/pdf_renderer.js +54 -0
- package/dist/report/pdf_renderer.js.map +1 -0
- package/dist/report/report_data.d.ts +128 -0
- package/dist/report/report_data.d.ts.map +1 -0
- package/dist/report/report_data.js +191 -0
- package/dist/report/report_data.js.map +1 -0
- package/dist/schema/edge.d.ts +40 -5
- package/dist/schema/edge.d.ts.map +1 -1
- package/dist/schema/edge.js +73 -0
- package/dist/schema/edge.js.map +1 -1
- package/dist/schema/node.d.ts +20 -5
- package/dist/schema/node.d.ts.map +1 -1
- package/dist/schema/node.js +36 -0
- package/dist/schema/node.js.map +1 -1
- package/dist/schema/runtime_manifest.d.ts +36 -0
- package/dist/schema/runtime_manifest.d.ts.map +1 -0
- package/dist/schema/runtime_manifest.js +23 -0
- package/dist/schema/runtime_manifest.js.map +1 -0
- package/dist/schema/source_manifest.d.ts +30 -0
- package/dist/schema/source_manifest.d.ts.map +1 -0
- package/dist/schema/source_manifest.js +21 -0
- package/dist/schema/source_manifest.js.map +1 -0
- package/dist/store/jsonl_reader.d.ts +4 -0
- package/dist/store/jsonl_reader.d.ts.map +1 -1
- package/dist/store/jsonl_reader.js +13 -1
- package/dist/store/jsonl_reader.js.map +1 -1
- package/dist/store/jsonl_store.d.ts +2 -1
- package/dist/store/jsonl_store.d.ts.map +1 -1
- package/dist/store/jsonl_store.js +4 -1
- package/dist/store/jsonl_store.js.map +1 -1
- package/dist/store/kuzu_store.d.ts +59 -0
- package/dist/store/kuzu_store.d.ts.map +1 -1
- package/dist/store/kuzu_store.js +124 -5
- package/dist/store/kuzu_store.js.map +1 -1
- package/dist/store/output_folder.d.ts +43 -0
- package/dist/store/output_folder.d.ts.map +1 -0
- package/dist/store/output_folder.js +61 -0
- package/dist/store/output_folder.js.map +1 -0
- package/dist/verify/project_verifier.d.ts +85 -0
- package/dist/verify/project_verifier.d.ts.map +1 -0
- package/dist/verify/project_verifier.js +138 -0
- package/dist/verify/project_verifier.js.map +1 -0
- package/dotclaude_folder/commands/code-graph-interview.md +123 -0
- package/dotclaude_folder/commands/code-graph-optimize.md +65 -0
- package/{skills/ts-knowledge-graph → dotclaude_folder/skills/code-graph-query}/SKILL.md +6 -6
- package/package.json +99 -10
- package/.env-sample +0 -34
- package/contribs/web_visualisation/README.md +0 -55
- package/contribs/web_visualisation/web/css/style.css +0 -115
- package/contribs/web_visualisation/web/data/.gitignore +0 -2
- package/contribs/web_visualisation/web/index.html +0 -58
- package/contribs/web_visualisation/web/js/app.js +0 -364
- package/dist/agent/agent-tools.d.ts +0 -13
- package/dist/agent/agent-tools.d.ts.map +0 -1
- package/dist/agent/agent-tools.js +0 -153
- package/dist/agent/agent-tools.js.map +0 -1
- package/dist/agent/agent_tools.d.ts +0 -13
- package/dist/agent/agent_tools.d.ts.map +0 -1
- package/dist/agent/agent_tools.js +0 -153
- package/dist/agent/agent_tools.js.map +0 -1
- package/dist/agent/code-editor.d.ts +0 -18
- package/dist/agent/code-editor.d.ts.map +0 -1
- package/dist/agent/code-editor.js +0 -43
- package/dist/agent/code-editor.js.map +0 -1
- package/dist/agent/code_editor.d.ts +0 -18
- package/dist/agent/code_editor.d.ts.map +0 -1
- package/dist/agent/code_editor.js +0 -43
- package/dist/agent/code_editor.js.map +0 -1
- package/dist/agent/optimizer-agent.d.ts +0 -30
- package/dist/agent/optimizer-agent.d.ts.map +0 -1
- package/dist/agent/optimizer-agent.js +0 -97
- package/dist/agent/optimizer-agent.js.map +0 -1
- package/dist/agent/optimizer_agent.d.ts +0 -30
- package/dist/agent/optimizer_agent.d.ts.map +0 -1
- package/dist/agent/optimizer_agent.js +0 -97
- package/dist/agent/optimizer_agent.js.map +0 -1
- package/dist/agent/verifier.d.ts +0 -9
- package/dist/agent/verifier.d.ts.map +0 -1
- package/dist/agent/verifier.js +0 -19
- package/dist/agent/verifier.js.map +0 -1
- package/dist/commands/blast-radius.d.ts +0 -5
- package/dist/commands/blast-radius.d.ts.map +0 -1
- package/dist/commands/blast-radius.js +0 -18
- package/dist/commands/blast-radius.js.map +0 -1
- package/dist/commands/blast_radius.d.ts +0 -5
- package/dist/commands/blast_radius.d.ts.map +0 -1
- package/dist/commands/blast_radius.js +0 -18
- package/dist/commands/blast_radius.js.map +0 -1
- package/dist/commands/calls.d.ts +0 -5
- package/dist/commands/calls.d.ts.map +0 -1
- package/dist/commands/calls.js +0 -7
- package/dist/commands/calls.js.map +0 -1
- package/dist/commands/command-helpers.d.ts +0 -15
- package/dist/commands/command-helpers.d.ts.map +0 -1
- package/dist/commands/command-helpers.js +0 -61
- package/dist/commands/command-helpers.js.map +0 -1
- package/dist/commands/dead-exports.d.ts +0 -5
- package/dist/commands/dead-exports.d.ts.map +0 -1
- package/dist/commands/dead-exports.js +0 -7
- package/dist/commands/dead-exports.js.map +0 -1
- package/dist/commands/dead_exports.d.ts +0 -5
- package/dist/commands/dead_exports.d.ts.map +0 -1
- package/dist/commands/dead_exports.js +0 -7
- package/dist/commands/dead_exports.js.map +0 -1
- package/dist/commands/extract.d.ts +0 -8
- package/dist/commands/extract.d.ts.map +0 -1
- package/dist/commands/extract.js +0 -49
- package/dist/commands/extract.js.map +0 -1
- package/dist/commands/find.d.ts +0 -5
- package/dist/commands/find.d.ts.map +0 -1
- package/dist/commands/find.js +0 -7
- package/dist/commands/find.js.map +0 -1
- package/dist/commands/load.d.ts.map +0 -1
- package/dist/commands/load.js +0 -28
- package/dist/commands/load.js.map +0 -1
- package/dist/commands/neighbors.d.ts +0 -5
- package/dist/commands/neighbors.d.ts.map +0 -1
- package/dist/commands/neighbors.js +0 -17
- package/dist/commands/neighbors.js.map +0 -1
- package/dist/commands/optimize.d.ts +0 -6
- package/dist/commands/optimize.d.ts.map +0 -1
- package/dist/commands/optimize.js +0 -59
- package/dist/commands/optimize.js.map +0 -1
- package/dist/commands/optimize_command.d.ts +0 -6
- package/dist/commands/optimize_command.d.ts.map +0 -1
- package/dist/commands/optimize_command.js +0 -59
- package/dist/commands/optimize_command.js.map +0 -1
- package/dist/commands/references.d.ts +0 -5
- package/dist/commands/references.d.ts.map +0 -1
- package/dist/commands/references.js +0 -17
- package/dist/commands/references.js.map +0 -1
- package/dist/commands/web.d.ts +0 -19
- package/dist/commands/web.d.ts.map +0 -1
- package/dist/commands/web.js +0 -120
- package/dist/commands/web.js.map +0 -1
- package/dist/commands/who-calls.d.ts +0 -5
- package/dist/commands/who-calls.d.ts.map +0 -1
- package/dist/commands/who-calls.js +0 -7
- package/dist/commands/who-calls.js.map +0 -1
- package/dist/commands/who_calls.d.ts +0 -5
- package/dist/commands/who_calls.d.ts.map +0 -1
- package/dist/commands/who_calls.js +0 -7
- package/dist/commands/who_calls.js.map +0 -1
- package/dist/extract/graph-builder.d.ts +0 -16
- package/dist/extract/graph-builder.d.ts.map +0 -1
- package/dist/extract/graph-builder.js +0 -39
- package/dist/extract/graph-builder.js.map +0 -1
- package/dist/extract/node-id.d.ts +0 -8
- package/dist/extract/node-id.d.ts.map +0 -1
- package/dist/extract/node-id.js +0 -22
- package/dist/extract/node-id.js.map +0 -1
- package/dist/extract/project-loader.d.ts +0 -5
- package/dist/extract/project-loader.d.ts.map +0 -1
- package/dist/extract/project-loader.js +0 -19
- package/dist/extract/project-loader.js.map +0 -1
- package/dist/extract/semantic-extractor.d.ts +0 -22
- package/dist/extract/semantic-extractor.d.ts.map +0 -1
- package/dist/extract/semantic-extractor.js +0 -254
- package/dist/extract/semantic-extractor.js.map +0 -1
- package/dist/extract/structural-extractor.d.ts +0 -18
- package/dist/extract/structural-extractor.d.ts.map +0 -1
- package/dist/extract/structural-extractor.js +0 -97
- package/dist/extract/structural-extractor.js.map +0 -1
- package/dist/query/graph-query.d.ts +0 -28
- package/dist/query/graph-query.d.ts.map +0 -1
- package/dist/query/graph-query.js +0 -93
- package/dist/query/graph-query.js.map +0 -1
- package/dist/store/jsonl-reader.d.ts +0 -11
- package/dist/store/jsonl-reader.d.ts.map +0 -1
- package/dist/store/jsonl-reader.js +0 -19
- package/dist/store/jsonl-reader.js.map +0 -1
- package/dist/store/jsonl-store.d.ts +0 -7
- package/dist/store/jsonl-store.d.ts.map +0 -1
- package/dist/store/jsonl-store.js +0 -13
- package/dist/store/jsonl-store.js.map +0 -1
- package/dist/store/kuzu-store.d.ts +0 -14
- package/dist/store/kuzu-store.d.ts.map +0 -1
- package/dist/store/kuzu-store.js +0 -52
- package/dist/store/kuzu-store.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { NodeId } from './node_id.js';
|
|
3
|
+
import { ScopeResolver } from './scope_resolver.js';
|
|
4
|
+
/**
|
|
5
|
+
* Detects configuration reads — `process.env.NAME` and `process.env['NAME']` —
|
|
6
|
+
* and emits one `ConfigFlag` node per distinct variable plus a `READS_CONFIG`
|
|
7
|
+
* edge from the enclosing declaration that performs the read. A variable read in
|
|
8
|
+
* several places collapses to one node (keyed by name) with one counted edge per
|
|
9
|
+
* reading scope.
|
|
10
|
+
*
|
|
11
|
+
* The detection is purely syntactic (no symbol resolution), so it runs in the
|
|
12
|
+
* structural pass and is always emitted. A project that never touches
|
|
13
|
+
* `process.env` produces no config nodes or edges, leaving its graph unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export class ConfigExtractor {
|
|
16
|
+
static extract(sourceFile, rootPath) {
|
|
17
|
+
const nodes = [];
|
|
18
|
+
const edges = [];
|
|
19
|
+
const moduleId = NodeId.forModule(sourceFile.getFilePath(), rootPath);
|
|
20
|
+
for (const access of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)) {
|
|
21
|
+
if (ConfigExtractor.isProcessEnv(access.getExpression()) === true) {
|
|
22
|
+
ConfigExtractor.emit(access, access.getName(), moduleId, rootPath, nodes, edges);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const access of sourceFile.getDescendantsOfKind(SyntaxKind.ElementAccessExpression)) {
|
|
26
|
+
if (ConfigExtractor.isProcessEnv(access.getExpression()) === false) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const name = ConfigExtractor.stringArgument(access);
|
|
30
|
+
if (name !== undefined) {
|
|
31
|
+
ConfigExtractor.emit(access, name, moduleId, rootPath, nodes, edges);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { nodes, edges };
|
|
35
|
+
}
|
|
36
|
+
static emit(access, name, moduleId, rootPath, nodes, edges) {
|
|
37
|
+
if (name === '') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const flagId = NodeId.forConfigFlag(name);
|
|
41
|
+
nodes.push({ id: flagId, kind: 'ConfigFlag', name, filePath: 'process.env' });
|
|
42
|
+
const scopeId = ScopeResolver.enclosingId(access, moduleId, rootPath);
|
|
43
|
+
edges.push({ id: `READS_CONFIG:${scopeId}->${flagId}`, kind: 'READS_CONFIG', from: scopeId, to: flagId });
|
|
44
|
+
}
|
|
45
|
+
/** Whether a node is the `process.env` member access (`process` identifier, `env` name). */
|
|
46
|
+
static isProcessEnv(node) {
|
|
47
|
+
const access = node.asKind(SyntaxKind.PropertyAccessExpression);
|
|
48
|
+
if (access === undefined || access.getName() !== 'env') {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const target = access.getExpression();
|
|
52
|
+
return Node.isIdentifier(target) === true && target.getText() === 'process';
|
|
53
|
+
}
|
|
54
|
+
/** The literal key of `process.env['NAME']`, or undefined when the key is computed. */
|
|
55
|
+
static stringArgument(access) {
|
|
56
|
+
const element = access.asKind(SyntaxKind.ElementAccessExpression);
|
|
57
|
+
const literal = element?.getArgumentExpression()?.asKind(SyntaxKind.StringLiteral);
|
|
58
|
+
return literal?.getLiteralText();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config_extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config_extractor.js","sourceRoot":"","sources":["../../src/extract/config_extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SourceFile } from 'ts-morph';
|
|
2
|
+
import { Extraction } from './structural_extractor.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detects HTTP route registrations in the Express/Fastify style —
|
|
5
|
+
* `app.get('/path', handler)`, `router.post('/path', mw, handler)` — and emits an
|
|
6
|
+
* `Endpoint` node (keyed by method + path) plus a `HANDLES` edge from the endpoint
|
|
7
|
+
* to the function that handles it.
|
|
8
|
+
*
|
|
9
|
+
* A route is recognised syntactically: the callee is `<obj>.<verb>` for an HTTP
|
|
10
|
+
* verb, the first argument is a string-literal path, and the last argument is a
|
|
11
|
+
* handler — an inline function, or a name/member that resolves to an in-project
|
|
12
|
+
* callable. Resolving the named handler is why this runs in the semantic pass; an
|
|
13
|
+
* inline handler yields the `Endpoint` node alone (it has no declaration to point
|
|
14
|
+
* at).
|
|
15
|
+
*
|
|
16
|
+
* The match is a heuristic (it does not prove `obj` is an Express app), but the
|
|
17
|
+
* verb + string path + resolvable-handler combination is specific, so a project
|
|
18
|
+
* with no such call sites is unchanged. Other routers (Nest decorators, …) are out
|
|
19
|
+
* of scope for now — Express/Fastify first (#31 Part 3).
|
|
20
|
+
*/
|
|
21
|
+
export declare class EndpointExtractor {
|
|
22
|
+
static extract(sourceFile: SourceFile, rootPath: string): Extraction;
|
|
23
|
+
private static extractRoute;
|
|
24
|
+
/**
|
|
25
|
+
* Classifies the last argument of a route call. An inline function is a valid
|
|
26
|
+
* handler with no node to point at; a name/member that resolves to an in-project
|
|
27
|
+
* callable is a valid handler whose emitted declaration becomes the `HANDLES`
|
|
28
|
+
* target; anything else means this is not a route.
|
|
29
|
+
*/
|
|
30
|
+
private static classifyHandler;
|
|
31
|
+
/** Resolves a handler name/member to the in-project function, method, or function-valued variable it refers to. */
|
|
32
|
+
private static resolveCallable;
|
|
33
|
+
private static resolve;
|
|
34
|
+
private static inProject;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=endpoint_extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint_extractor.d.ts","sourceRoot":"","sources":["../../src/extract/endpoint_extractor.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,UAAU,EAAc,MAAM,UAAU,CAAC;AAKxE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAQvD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAiB;IAC7B,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IASpE,OAAO,CAAC,MAAM,CAAC,YAAY;IAoC3B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAc9B,mHAAmH;IACnH,OAAO,CAAC,MAAM,CAAC,eAAe;IAkB9B,OAAO,CAAC,MAAM,CAAC,OAAO;IAMtB,OAAO,CAAC,MAAM,CAAC,SAAS;CAKxB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
3
|
+
import { NodeId } from './node_id.js';
|
|
4
|
+
import { ScopeResolver } from './scope_resolver.js';
|
|
5
|
+
/** HTTP-verb method names that register a route on an Express/Fastify app or router. */
|
|
6
|
+
const HTTP_METHODS = new Set(['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'all']);
|
|
7
|
+
/**
|
|
8
|
+
* Detects HTTP route registrations in the Express/Fastify style —
|
|
9
|
+
* `app.get('/path', handler)`, `router.post('/path', mw, handler)` — and emits an
|
|
10
|
+
* `Endpoint` node (keyed by method + path) plus a `HANDLES` edge from the endpoint
|
|
11
|
+
* to the function that handles it.
|
|
12
|
+
*
|
|
13
|
+
* A route is recognised syntactically: the callee is `<obj>.<verb>` for an HTTP
|
|
14
|
+
* verb, the first argument is a string-literal path, and the last argument is a
|
|
15
|
+
* handler — an inline function, or a name/member that resolves to an in-project
|
|
16
|
+
* callable. Resolving the named handler is why this runs in the semantic pass; an
|
|
17
|
+
* inline handler yields the `Endpoint` node alone (it has no declaration to point
|
|
18
|
+
* at).
|
|
19
|
+
*
|
|
20
|
+
* The match is a heuristic (it does not prove `obj` is an Express app), but the
|
|
21
|
+
* verb + string path + resolvable-handler combination is specific, so a project
|
|
22
|
+
* with no such call sites is unchanged. Other routers (Nest decorators, …) are out
|
|
23
|
+
* of scope for now — Express/Fastify first (#31 Part 3).
|
|
24
|
+
*/
|
|
25
|
+
export class EndpointExtractor {
|
|
26
|
+
static extract(sourceFile, rootPath) {
|
|
27
|
+
const nodes = [];
|
|
28
|
+
const edges = [];
|
|
29
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
30
|
+
EndpointExtractor.extractRoute(call, rootPath, nodes, edges);
|
|
31
|
+
}
|
|
32
|
+
return { nodes, edges };
|
|
33
|
+
}
|
|
34
|
+
static extractRoute(call, rootPath, nodes, edges) {
|
|
35
|
+
const callee = call.getExpression().asKind(SyntaxKind.PropertyAccessExpression);
|
|
36
|
+
if (callee === undefined || HTTP_METHODS.has(callee.getName()) === false) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const args = call.getArguments();
|
|
40
|
+
if (args.length < 2) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const pathLiteral = args[0].asKind(SyntaxKind.StringLiteral);
|
|
44
|
+
if (pathLiteral === undefined) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const handler = EndpointExtractor.classifyHandler(args[args.length - 1], rootPath);
|
|
48
|
+
if (handler.valid === false) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const method = callee.getName().toUpperCase();
|
|
52
|
+
const endpointId = NodeId.forEndpoint(method, pathLiteral.getLiteralText());
|
|
53
|
+
nodes.push({
|
|
54
|
+
id: endpointId,
|
|
55
|
+
kind: 'Endpoint',
|
|
56
|
+
name: `${method} ${pathLiteral.getLiteralText()}`,
|
|
57
|
+
filePath: relative(rootPath, call.getSourceFile().getFilePath()),
|
|
58
|
+
range: {
|
|
59
|
+
startLine: call.getStartLineNumber(),
|
|
60
|
+
startColumn: 0,
|
|
61
|
+
endLine: call.getEndLineNumber(),
|
|
62
|
+
endColumn: 0,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (handler.id !== undefined) {
|
|
66
|
+
edges.push({ id: `HANDLES:${endpointId}->${handler.id}`, kind: 'HANDLES', from: endpointId, to: handler.id });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Classifies the last argument of a route call. An inline function is a valid
|
|
71
|
+
* handler with no node to point at; a name/member that resolves to an in-project
|
|
72
|
+
* callable is a valid handler whose emitted declaration becomes the `HANDLES`
|
|
73
|
+
* target; anything else means this is not a route.
|
|
74
|
+
*/
|
|
75
|
+
static classifyHandler(handler, rootPath) {
|
|
76
|
+
if (Node.isArrowFunction(handler) === true || Node.isFunctionExpression(handler) === true) {
|
|
77
|
+
return { valid: true };
|
|
78
|
+
}
|
|
79
|
+
const declaration = EndpointExtractor.resolveCallable(handler);
|
|
80
|
+
if (declaration === undefined) {
|
|
81
|
+
return { valid: false };
|
|
82
|
+
}
|
|
83
|
+
if (ScopeResolver.isEmitted(declaration) === true) {
|
|
84
|
+
return { valid: true, id: NodeId.forDeclaration(declaration, rootPath) };
|
|
85
|
+
}
|
|
86
|
+
return { valid: true };
|
|
87
|
+
}
|
|
88
|
+
/** Resolves a handler name/member to the in-project function, method, or function-valued variable it refers to. */
|
|
89
|
+
static resolveCallable(node) {
|
|
90
|
+
const declaration = EndpointExtractor.resolve(node);
|
|
91
|
+
if (declaration === undefined || EndpointExtractor.inProject(declaration) === false) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const kind = declaration.getKind();
|
|
95
|
+
if (kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MethodDeclaration) {
|
|
96
|
+
return declaration;
|
|
97
|
+
}
|
|
98
|
+
if (kind === SyntaxKind.VariableDeclaration) {
|
|
99
|
+
const initializer = declaration.asKind(SyntaxKind.VariableDeclaration)?.getInitializer();
|
|
100
|
+
if (initializer !== undefined && (Node.isArrowFunction(initializer) === true || Node.isFunctionExpression(initializer) === true)) {
|
|
101
|
+
return declaration;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
static resolve(node) {
|
|
107
|
+
const symbol = node.getSymbol();
|
|
108
|
+
const resolved = symbol?.getAliasedSymbol() ?? symbol;
|
|
109
|
+
return resolved?.getDeclarations()[0];
|
|
110
|
+
}
|
|
111
|
+
static inProject(node) {
|
|
112
|
+
const sourceFile = node.getSourceFile();
|
|
113
|
+
return sourceFile.getFilePath().includes('/node_modules/') === false
|
|
114
|
+
&& sourceFile.isDeclarationFile() === false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=endpoint_extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint_extractor.js","sourceRoot":"","sources":["../../src/extract/endpoint_extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SourceManifest } from '../schema/source_manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the Git provenance of an analysed project — its GitHub repository, the
|
|
4
|
+
* commit being parsed, and the project root's path within the repository — so the
|
|
5
|
+
* graph can later link each file to its exact source on GitHub.
|
|
6
|
+
*/
|
|
7
|
+
export declare class GitSource {
|
|
8
|
+
/**
|
|
9
|
+
* Detects the GitHub repository, HEAD commit, and in-repo path prefix for `dir`.
|
|
10
|
+
* Returns `undefined` when `dir` is not a Git work tree, has no GitHub `origin`
|
|
11
|
+
* remote, or Git is unavailable.
|
|
12
|
+
*/
|
|
13
|
+
static detect(dir: string): Promise<SourceManifest | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Normalises a Git `origin` URL to its GitHub web base
|
|
16
|
+
* (`https://<host>/<owner>/<repo>`), or `undefined` for non-GitHub remotes.
|
|
17
|
+
* Handles the SCP-like (`git@host:owner/repo.git`), `https://`, `git://`, and
|
|
18
|
+
* `ssh://` forms, with or without a trailing `.git`. The host is kept as-is so
|
|
19
|
+
* GitHub Enterprise remotes resolve to their own domain.
|
|
20
|
+
*/
|
|
21
|
+
static githubBaseUrl(remoteUrl: string): string | undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=git_source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git_source.d.ts","sourceRoot":"","sources":["../../src/extract/git_source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D;;;;GAIG;AACH,qBAAa,SAAS;IACrB;;;;OAIG;WACU,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAsBrE;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CA6B3D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the Git provenance of an analysed project — its GitHub repository, the
|
|
6
|
+
* commit being parsed, and the project root's path within the repository — so the
|
|
7
|
+
* graph can later link each file to its exact source on GitHub.
|
|
8
|
+
*/
|
|
9
|
+
export class GitSource {
|
|
10
|
+
/**
|
|
11
|
+
* Detects the GitHub repository, HEAD commit, and in-repo path prefix for `dir`.
|
|
12
|
+
* Returns `undefined` when `dir` is not a Git work tree, has no GitHub `origin`
|
|
13
|
+
* remote, or Git is unavailable.
|
|
14
|
+
*/
|
|
15
|
+
static async detect(dir) {
|
|
16
|
+
const git = async (...args) => {
|
|
17
|
+
try {
|
|
18
|
+
const { stdout } = await execFileAsync('git', ['-C', dir, ...args]);
|
|
19
|
+
return stdout.trim();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
if (await git('rev-parse', '--is-inside-work-tree') !== 'true') {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const remoteUrl = await git('remote', 'get-url', 'origin');
|
|
29
|
+
const commit = await git('rev-parse', 'HEAD');
|
|
30
|
+
const baseUrl = remoteUrl === undefined ? undefined : GitSource.githubBaseUrl(remoteUrl);
|
|
31
|
+
if (baseUrl === undefined || commit === undefined) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return { baseUrl, commit, prefix: await git('rev-parse', '--show-prefix') ?? '' };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Normalises a Git `origin` URL to its GitHub web base
|
|
38
|
+
* (`https://<host>/<owner>/<repo>`), or `undefined` for non-GitHub remotes.
|
|
39
|
+
* Handles the SCP-like (`git@host:owner/repo.git`), `https://`, `git://`, and
|
|
40
|
+
* `ssh://` forms, with or without a trailing `.git`. The host is kept as-is so
|
|
41
|
+
* GitHub Enterprise remotes resolve to their own domain.
|
|
42
|
+
*/
|
|
43
|
+
static githubBaseUrl(remoteUrl) {
|
|
44
|
+
const trimmed = remoteUrl.trim();
|
|
45
|
+
let host;
|
|
46
|
+
let path;
|
|
47
|
+
if (trimmed.includes('://') === true) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = new URL(trimmed);
|
|
50
|
+
host = parsed.host;
|
|
51
|
+
path = parsed.pathname;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const scpMatch = trimmed.match(/^[^@]+@([^:]+):(.+)$/);
|
|
59
|
+
if (scpMatch === null) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
host = scpMatch[1];
|
|
63
|
+
path = scpMatch[2];
|
|
64
|
+
}
|
|
65
|
+
if (host.toLowerCase().includes('github') === false) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const segments = path.replace(/\.git$/, '').split('/').filter((segment) => segment.length > 0);
|
|
69
|
+
if (segments.length < 2) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return `https://${host}/${segments[0]}/${segments[1]}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=git_source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git_source.js","sourceRoot":"","sources":["../../src/extract/git_source.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,14 @@ export declare class GraphBuilder {
|
|
|
11
11
|
getNodes(): GraphNode[];
|
|
12
12
|
getEdges(): GraphEdge[];
|
|
13
13
|
private merge;
|
|
14
|
+
/**
|
|
15
|
+
* Adds an edge, collapsing duplicates by id while counting how many times the
|
|
16
|
+
* relationship occurs in source. The first occurrence is stored with
|
|
17
|
+
* `metadata.count = 1`; each later occurrence increments that count instead of
|
|
18
|
+
* overwriting the edge. Any pre-existing metadata (such as the `specifier` on
|
|
19
|
+
* an `IMPORTS` edge) is preserved.
|
|
20
|
+
*/
|
|
21
|
+
private addEdge;
|
|
14
22
|
private static isProjectFile;
|
|
15
23
|
}
|
|
16
24
|
//# sourceMappingURL=graph_builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_builder.d.ts","sourceRoot":"","sources":["../../src/extract/graph_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"graph_builder.d.ts","sourceRoot":"","sources":["../../src/extract/graph_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAO9C,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IAEtD,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAkBtE,QAAQ,IAAI,SAAS,EAAE;IAIvB,QAAQ,IAAI,SAAS,EAAE;IAIvB,OAAO,CAAC,KAAK;IASb;;;;;;OAMG;IACH,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,MAAM,CAAC,aAAa;CAG5B"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ApiExtractor } from './api_extractor.js';
|
|
2
|
+
import { ConfigExtractor } from './config_extractor.js';
|
|
3
|
+
import { EndpointExtractor } from './endpoint_extractor.js';
|
|
1
4
|
import { SemanticExtractor } from './semantic_extractor.js';
|
|
2
5
|
import { StructuralExtractor } from './structural_extractor.js';
|
|
3
6
|
export class GraphBuilder {
|
|
@@ -11,10 +14,13 @@ export class GraphBuilder {
|
|
|
11
14
|
.filter((file) => GraphBuilder.isProjectFile(file.getFilePath()));
|
|
12
15
|
for (const sourceFile of sourceFiles) {
|
|
13
16
|
this.merge(StructuralExtractor.extract(sourceFile, rootPath));
|
|
17
|
+
this.merge(ConfigExtractor.extract(sourceFile, rootPath));
|
|
18
|
+
this.merge(ApiExtractor.extract(sourceFile, rootPath));
|
|
14
19
|
}
|
|
15
20
|
if (options.semantic === true) {
|
|
16
21
|
for (const sourceFile of sourceFiles) {
|
|
17
22
|
this.merge(SemanticExtractor.extract(sourceFile, rootPath));
|
|
23
|
+
this.merge(EndpointExtractor.extract(sourceFile, rootPath));
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
26
|
}
|
|
@@ -29,9 +35,25 @@ export class GraphBuilder {
|
|
|
29
35
|
this.nodes.set(node.id, node);
|
|
30
36
|
}
|
|
31
37
|
for (const edge of extraction.edges) {
|
|
32
|
-
this.
|
|
38
|
+
this.addEdge(edge);
|
|
33
39
|
}
|
|
34
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Adds an edge, collapsing duplicates by id while counting how many times the
|
|
43
|
+
* relationship occurs in source. The first occurrence is stored with
|
|
44
|
+
* `metadata.count = 1`; each later occurrence increments that count instead of
|
|
45
|
+
* overwriting the edge. Any pre-existing metadata (such as the `specifier` on
|
|
46
|
+
* an `IMPORTS` edge) is preserved.
|
|
47
|
+
*/
|
|
48
|
+
addEdge(edge) {
|
|
49
|
+
const existing = this.edges.get(edge.id);
|
|
50
|
+
if (existing === undefined) {
|
|
51
|
+
this.edges.set(edge.id, { ...edge, metadata: { ...edge.metadata, count: 1 } });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const current = typeof existing.metadata?.count === 'number' ? existing.metadata.count : 1;
|
|
55
|
+
existing.metadata = { ...existing.metadata, count: current + 1 };
|
|
56
|
+
}
|
|
35
57
|
static isProjectFile(filePath) {
|
|
36
58
|
return filePath.includes('/node_modules/') === false && filePath.endsWith('.d.ts') === false;
|
|
37
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_builder.js","sourceRoot":"","sources":["../../src/extract/graph_builder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAc,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAM5E,MAAM,OAAO,YAAY;IAAzB;QACkB,UAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;QACrC,UAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"graph_builder.js","sourceRoot":"","sources":["../../src/extract/graph_builder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAc,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAM5E,MAAM,OAAO,YAAY;IAAzB;QACkB,UAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;QACrC,UAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAyDvD,CAAC;IAvDA,KAAK,CAAC,OAAgB,EAAE,QAAgB,EAAE,OAAqB;QAC9D,MAAM,WAAW,GAAG,OAAO;aACzB,cAAc,EAAE;aAChB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAEnE,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC9D,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC1D,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC/B,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACtC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAC5D,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC7D,CAAC;QACF,CAAC;IACF,CAAC;IAED,QAAQ;QACP,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,QAAQ;QACP,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC;IAEO,KAAK,CAAC,UAAsB;QACnC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACK,OAAO,CAAC,IAAe;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/E,OAAO;QACR,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,QAAQ,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3F,QAAQ,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAAC;IAClE,CAAC;IAEO,MAAM,CAAC,aAAa,CAAC,QAAgB;QAC5C,OAAO,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC;IAC9F,CAAC;CACD"}
|
|
@@ -3,6 +3,22 @@ export declare class NodeId {
|
|
|
3
3
|
static forModule(filePath: string, rootPath: string): string;
|
|
4
4
|
static forDeclaration(node: Node, rootPath: string): string;
|
|
5
5
|
static forExternalModule(specifier: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* A graph-wide id for a configuration variable (e.g. `process.env.PORT`), keyed
|
|
8
|
+
* by name so the same variable read across many files collapses to one node.
|
|
9
|
+
*/
|
|
10
|
+
static forConfigFlag(name: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* A graph-wide id for an external HTTP target, keyed by host (e.g. the
|
|
13
|
+
* `api.example.com` of a `fetch('https://api.example.com/…')`) so every call to
|
|
14
|
+
* the same service collapses to one node.
|
|
15
|
+
*/
|
|
16
|
+
static forExternalApi(host: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* A graph-wide id for an HTTP endpoint, keyed by method and route path (e.g.
|
|
19
|
+
* `Endpoint:GET /users/:id`) so a route registered once is one node.
|
|
20
|
+
*/
|
|
21
|
+
static forEndpoint(method: string, path: string): string;
|
|
6
22
|
static nameOf(node: Node): string;
|
|
7
23
|
}
|
|
8
24
|
//# sourceMappingURL=node_id.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node_id.d.ts","sourceRoot":"","sources":["../../src/extract/node_id.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,qBAAa,MAAM;IAClB,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5D,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAK3D,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAInD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;CAQjC"}
|
|
1
|
+
{"version":3,"file":"node_id.d.ts","sourceRoot":"","sources":["../../src/extract/node_id.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,qBAAa,MAAM;IAClB,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5D,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAK3D,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAInD;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI1C;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI3C;;;OAGG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAIxD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;CAQjC"}
|
package/dist/extract/node_id.js
CHANGED
|
@@ -10,6 +10,28 @@ export class NodeId {
|
|
|
10
10
|
static forExternalModule(specifier) {
|
|
11
11
|
return `External:${specifier}`;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* A graph-wide id for a configuration variable (e.g. `process.env.PORT`), keyed
|
|
15
|
+
* by name so the same variable read across many files collapses to one node.
|
|
16
|
+
*/
|
|
17
|
+
static forConfigFlag(name) {
|
|
18
|
+
return `Config:${name}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A graph-wide id for an external HTTP target, keyed by host (e.g. the
|
|
22
|
+
* `api.example.com` of a `fetch('https://api.example.com/…')`) so every call to
|
|
23
|
+
* the same service collapses to one node.
|
|
24
|
+
*/
|
|
25
|
+
static forExternalApi(host) {
|
|
26
|
+
return `Api:${host}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A graph-wide id for an HTTP endpoint, keyed by method and route path (e.g.
|
|
30
|
+
* `Endpoint:GET /users/:id`) so a route registered once is one node.
|
|
31
|
+
*/
|
|
32
|
+
static forEndpoint(method, path) {
|
|
33
|
+
return `Endpoint:${method} ${path}`;
|
|
34
|
+
}
|
|
13
35
|
static nameOf(node) {
|
|
14
36
|
const probe = node;
|
|
15
37
|
if (typeof probe.getName !== 'function') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node_id.js","sourceRoot":"","sources":["../../src/extract/node_id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,MAAM,OAAO,MAAM;IAClB,MAAM,CAAC,SAAS,CAAC,QAAgB,EAAE,QAAgB;QAClD,OAAO,UAAU,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,IAAU,EAAE,QAAgB;QACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,iBAAiB,CAAC,SAAiB;QACzC,OAAO,YAAY,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,IAAU;QACvB,MAAM,KAAK,GAAG,IAA8C,CAAC;QAC7D,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACzC,OAAO,WAAW,CAAC;QACpB,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7B,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,CAAC;CACD"}
|
|
1
|
+
{"version":3,"file":"node_id.js","sourceRoot":"","sources":["../../src/extract/node_id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,MAAM,OAAO,MAAM;IAClB,MAAM,CAAC,SAAS,CAAC,QAAgB,EAAE,QAAgB;QAClD,OAAO,UAAU,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,IAAU,EAAE,QAAgB;QACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,iBAAiB,CAAC,SAAiB;QACzC,OAAO,YAAY,SAAS,EAAE,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAY;QAChC,OAAO,UAAU,IAAI,EAAE,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,IAAY;QACjC,OAAO,OAAO,IAAI,EAAE,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,WAAW,CAAC,MAAc,EAAE,IAAY;QAC9C,OAAO,YAAY,MAAM,IAAI,IAAI,EAAE,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,IAAU;QACvB,MAAM,KAAK,GAAG,IAA8C,CAAC;QAC7D,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACzC,OAAO,WAAW,CAAC;QACpB,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7B,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,CAAC;CACD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Node } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the graph node a free-standing expression (a `process.env` read, a
|
|
4
|
+
* `fetch` call) should be attributed to: the id of the nearest enclosing
|
|
5
|
+
* declaration the structural extractor actually emits, falling back to the module.
|
|
6
|
+
*
|
|
7
|
+
* Because a node nested inside a function (a local, a nested function, an arrow) is
|
|
8
|
+
* not itself a graph node, the walk skips past it to the enclosing top-level
|
|
9
|
+
* function/variable/class or class member — so the returned id always exists in the
|
|
10
|
+
* graph and the edge built from it is never dropped at load.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ScopeResolver {
|
|
13
|
+
static enclosingId(node: Node, moduleId: string, rootPath: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the structural extractor emits this declaration as a graph node — a
|
|
16
|
+
* class/interface member, or a top-level function/variable/class/etc. Used both
|
|
17
|
+
* to find an enclosing scope and to check that a resolved handler is a real node
|
|
18
|
+
* before pointing an edge at it.
|
|
19
|
+
*/
|
|
20
|
+
static isEmitted(node: Node): boolean;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=scope_resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope_resolver.d.ts","sourceRoot":"","sources":["../../src/extract/scope_resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAc,MAAM,UAAU,CAAC;AAkB5C;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACzB,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAK1E;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;CAcrC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { NodeId } from './node_id.js';
|
|
3
|
+
/**
|
|
4
|
+
* Top-level declaration kinds the structural extractor emits as nodes. Used to
|
|
5
|
+
* attribute a free-standing expression to an *emitted* scope: a node nested inside
|
|
6
|
+
* a function (a local, a nested function) is not a graph node, so the walk skips
|
|
7
|
+
* past it to the nearest enclosing emitted declaration.
|
|
8
|
+
*/
|
|
9
|
+
const TOP_LEVEL_SCOPE_KINDS = new Set([
|
|
10
|
+
SyntaxKind.FunctionDeclaration,
|
|
11
|
+
SyntaxKind.VariableDeclaration,
|
|
12
|
+
SyntaxKind.ClassDeclaration,
|
|
13
|
+
SyntaxKind.InterfaceDeclaration,
|
|
14
|
+
SyntaxKind.EnumDeclaration,
|
|
15
|
+
SyntaxKind.TypeAliasDeclaration,
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the graph node a free-standing expression (a `process.env` read, a
|
|
19
|
+
* `fetch` call) should be attributed to: the id of the nearest enclosing
|
|
20
|
+
* declaration the structural extractor actually emits, falling back to the module.
|
|
21
|
+
*
|
|
22
|
+
* Because a node nested inside a function (a local, a nested function, an arrow) is
|
|
23
|
+
* not itself a graph node, the walk skips past it to the enclosing top-level
|
|
24
|
+
* function/variable/class or class member — so the returned id always exists in the
|
|
25
|
+
* graph and the edge built from it is never dropped at load.
|
|
26
|
+
*/
|
|
27
|
+
export class ScopeResolver {
|
|
28
|
+
static enclosingId(node, moduleId, rootPath) {
|
|
29
|
+
const scope = node.getFirstAncestor((ancestor) => ScopeResolver.isEmitted(ancestor));
|
|
30
|
+
return scope === undefined ? moduleId : NodeId.forDeclaration(scope, rootPath);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether the structural extractor emits this declaration as a graph node — a
|
|
34
|
+
* class/interface member, or a top-level function/variable/class/etc. Used both
|
|
35
|
+
* to find an enclosing scope and to check that a resolved handler is a real node
|
|
36
|
+
* before pointing an edge at it.
|
|
37
|
+
*/
|
|
38
|
+
static isEmitted(node) {
|
|
39
|
+
const kind = node.getKind();
|
|
40
|
+
if (kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.PropertyDeclaration) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (TOP_LEVEL_SCOPE_KINDS.has(kind) === false) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (kind === SyntaxKind.VariableDeclaration) {
|
|
47
|
+
const statement = node.asKind(SyntaxKind.VariableDeclaration)?.getVariableStatement();
|
|
48
|
+
return statement?.getParent()?.getKind() === SyntaxKind.SourceFile;
|
|
49
|
+
}
|
|
50
|
+
return node.getParent()?.getKind() === SyntaxKind.SourceFile;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=scope_resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope_resolver.js","sourceRoot":"","sources":["../../src/extract/scope_resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAa;IACjD,UAAU,CAAC,mBAAmB;IAC9B,UAAU,CAAC,mBAAmB;IAC9B,UAAU,CAAC,gBAAgB;IAC3B,UAAU,CAAC,oBAAoB;IAC/B,UAAU,CAAC,eAAe;IAC1B,UAAU,CAAC,oBAAoB;CAC/B,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IACzB,MAAM,CAAC,WAAW,CAAC,IAAU,EAAE,QAAgB,EAAE,QAAgB;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrF,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChF,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,IAAU;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,UAAU,CAAC,iBAAiB,IAAI,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,CAAC;YACtF,OAAO,IAAI,CAAC;QACb,CAAC;QACD,IAAI,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;YAC/C,OAAO,KAAK,CAAC;QACd,CAAC;QACD,IAAI,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,CAAC;YAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,oBAAoB,EAAE,CAAC;YACtF,OAAO,SAAS,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU,CAAC;IAC9D,CAAC;CACD"}
|
|
@@ -3,6 +3,14 @@ import { Extraction } from './structural_extractor.js';
|
|
|
3
3
|
export declare class SemanticExtractor {
|
|
4
4
|
static extract(sourceFile: SourceFile, rootPath: string): Extraction;
|
|
5
5
|
private static extractClass;
|
|
6
|
+
/**
|
|
7
|
+
* Emit an `OVERRIDES` edge from a class method to the member it overrides:
|
|
8
|
+
* the nearest base-class method of the same name, and any implemented
|
|
9
|
+
* interface method of the same name. The target id is computed with
|
|
10
|
+
* {@link NodeId.forDeclaration} on the resolved declaration, so it matches
|
|
11
|
+
* the node the structural extractor already emitted for that member.
|
|
12
|
+
*/
|
|
13
|
+
private static extractOverrides;
|
|
6
14
|
private static extractInterface;
|
|
7
15
|
private static extractSignature;
|
|
8
16
|
private static addTypeEdges;
|
|
@@ -10,7 +18,24 @@ export declare class SemanticExtractor {
|
|
|
10
18
|
private static extractCalls;
|
|
11
19
|
private static extractInstantiations;
|
|
12
20
|
private static extractReads;
|
|
21
|
+
private static extractWrites;
|
|
22
|
+
/**
|
|
23
|
+
* Emits a value-access edge from the enclosing scope to each in-project,
|
|
24
|
+
* module-level value declaration an identifier touches. `accesses` selects the
|
|
25
|
+
* identifiers (a read or a write) and `kind` is the edge emitted; the rest — the
|
|
26
|
+
* target must be an emitted top-level value declaration, a declaration's own name
|
|
27
|
+
* is skipped, self-edges are dropped — is shared by both. Repeated accesses
|
|
28
|
+
* collapse into one counted edge in {@link GraphBuilder}.
|
|
29
|
+
*/
|
|
30
|
+
private static extractValueAccess;
|
|
13
31
|
private static isValueRead;
|
|
32
|
+
/**
|
|
33
|
+
* Whether an identifier is written: the left-hand side of an assignment
|
|
34
|
+
* (`x = …`, `x += …`) or the operand of a `++`/`--`. A compound assignment and an
|
|
35
|
+
* increment also read the old value, so {@link isValueRead} reports those
|
|
36
|
+
* identifiers too; a plain `x = …` is a write only.
|
|
37
|
+
*/
|
|
38
|
+
private static isValueWrite;
|
|
14
39
|
private static isDeclarationName;
|
|
15
40
|
private static isEmittedTarget;
|
|
16
41
|
private static readerScope;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic_extractor.d.ts","sourceRoot":"","sources":["../../src/extract/semantic_extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,UAAU,EAGV,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"semantic_extractor.d.ts","sourceRoot":"","sources":["../../src/extract/semantic_extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,UAAU,EAGV,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAyDvD,qBAAa,iBAAiB;IAC7B,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAwBpE,OAAO,CAAC,MAAM,CAAC,YAAY;IAqB3B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAkC/B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAe/B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,YAAY;IAkB3B,OAAO,CAAC,MAAM,CAAC,eAAe;IAkB9B,OAAO,CAAC,MAAM,CAAC,YAAY;IAqB3B,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAqBpC,OAAO,CAAC,MAAM,CAAC,YAAY;IAI3B,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAiCjC,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAiB3B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAKhC,OAAO,CAAC,MAAM,CAAC,eAAe;IAQ9B,OAAO,CAAC,MAAM,CAAC,WAAW;IAW1B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAOnC,OAAO,CAAC,MAAM,CAAC,OAAO;IAUtB,OAAO,CAAC,MAAM,CAAC,SAAS;IAMxB,OAAO,CAAC,MAAM,CAAC,IAAI;CAGnB"}
|