@lowgular/code-graph 0.1.1 → 0.1.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/bin/cli.js +2458 -5
- package/lib.js +2430 -0
- package/package.json +3 -2
- package/code-graph-v2/code.graph.js +0 -37
- package/code-graph-v2/config-from-query.js +0 -131
- package/code-graph-v2/extractors/extractor.js +0 -27
- package/code-graph-v2/extractors/index.js +0 -1
- package/code-graph-v2/graph-builder/code-graph.builder.js +0 -49
- package/code-graph-v2/graph-builder/index.js +0 -1
- package/code-graph-v2/graph-builder/node.processor.js +0 -22
- package/code-graph-v2/graph-builder/relationship.processor.js +0 -55
- package/code-graph-v2/graph-builder/type.processor.js +0 -21
- package/code-graph-v2/index.js +0 -4
- package/code-graph-v2/tools/build-code-graph.tool.js +0 -19
- package/code-graph-v2/utils.js +0 -34
- package/codegular/index.js +0 -5
- package/codegular/node.js +0 -71
- package/codegular/program.js +0 -100
- package/codegular/string.js +0 -121
- package/codegular/type-checker.js +0 -133
- package/codegular/type.js +0 -356
- package/codegular/utils.js +0 -335
- package/cypher/index.js +0 -1
- package/cypher/lib/executor/condition-evaluator.js +0 -135
- package/cypher/lib/executor/executor.js +0 -60
- package/cypher/lib/executor/graph.js +0 -0
- package/cypher/lib/executor/match-engine.js +0 -130
- package/cypher/lib/executor/pattern-matcher.js +0 -86
- package/cypher/lib/executor/relationship-navigator.js +0 -41
- package/cypher/lib/executor/result-formatter.js +0 -149
- package/cypher/lib/executor/traverse-engine.js +0 -141
- package/cypher/lib/executor/utils.js +0 -14
- package/cypher/lib/graph.stub.js +0 -38
- package/cypher/lib/index.js +0 -32
- package/cypher/lib/lexer.js +0 -376
- package/cypher/lib/parser.js +0 -586
- package/cypher/lib/validator/query-validator.js +0 -75
- package/cypher/lib/validator/supported-features.config.js +0 -83
- package/cypher/lib/validator/unsupported-features.config.js +0 -124
- package/cypher-cli.js +0 -41
- package/infra/code-graph.js +0 -147
- package/main.js +0 -0
- package/resources-cli.js +0 -75
- package/run-cli.js +0 -43
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowgular/code-graph",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Run Cypher queries against a TypeScript code graph built from tsconfig",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./bin/cli.js",
|
|
8
8
|
"exports": {
|
|
9
|
-
".": "./bin/cli.js"
|
|
9
|
+
".": "./bin/cli.js",
|
|
10
|
+
"./lib": "./lib.js"
|
|
10
11
|
},
|
|
11
12
|
"bin": {
|
|
12
13
|
"code-graph": "./bin/cli.js"
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
const addNode = (graph, node) => {
|
|
2
|
-
graph.nodesById[node.id] = node;
|
|
3
|
-
return graph;
|
|
4
|
-
};
|
|
5
|
-
const getEdgeKey = (edge) => {
|
|
6
|
-
return `${edge.source}|${edge.target}|${edge.type}`;
|
|
7
|
-
};
|
|
8
|
-
const addEdge = (graph, edge) => {
|
|
9
|
-
const edgeKey = getEdgeKey(edge);
|
|
10
|
-
const sourceEdges = graph.edgesBySource[edge.source];
|
|
11
|
-
if (sourceEdges) {
|
|
12
|
-
const sourceEdgeKeys = new Set(sourceEdges.map(getEdgeKey));
|
|
13
|
-
if (sourceEdgeKeys.has(edgeKey)) {
|
|
14
|
-
return graph;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
const targetEdges = graph.edgesByTarget[edge.target];
|
|
18
|
-
if (targetEdges) {
|
|
19
|
-
const targetEdgeKeys = new Set(targetEdges.map(getEdgeKey));
|
|
20
|
-
if (targetEdgeKeys.has(edgeKey)) {
|
|
21
|
-
return graph;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (!graph.edgesBySource[edge.source]) {
|
|
25
|
-
graph.edgesBySource[edge.source] = [];
|
|
26
|
-
}
|
|
27
|
-
graph.edgesBySource[edge.source].push(edge);
|
|
28
|
-
if (!graph.edgesByTarget[edge.target]) {
|
|
29
|
-
graph.edgesByTarget[edge.target] = [];
|
|
30
|
-
}
|
|
31
|
-
graph.edgesByTarget[edge.target].push(edge);
|
|
32
|
-
return graph;
|
|
33
|
-
};
|
|
34
|
-
export {
|
|
35
|
-
addEdge,
|
|
36
|
-
addNode
|
|
37
|
-
};
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
const STANDARD_NODE_PROPERTIES = {
|
|
2
|
-
firstIdentifier: `helpers.getDescendantsBy(args.current, (node) => node.kind === args.ts.SyntaxKind.Identifier)[0]?.getText() ?? null`,
|
|
3
|
-
initializer: `args.current.initializer ? args.current.initializer?.getText() : null`,
|
|
4
|
-
name: `args.current.name?.getText() ?? null`,
|
|
5
|
-
filePath: `args.sourceFile.fileName`,
|
|
6
|
-
text: `args.current.getText()`,
|
|
7
|
-
type: `(() => { try { const t = args.typeChecker.getTypeAtLocation(args.current); return t ? args.typeChecker.typeToString(t) : null; } catch { return null; } })()`
|
|
8
|
-
};
|
|
9
|
-
const SUPPORTED_RELATIONSHIPS = {
|
|
10
|
-
// --- Traversal relationships ---
|
|
11
|
-
HAS_DESCENDANTS: (labels) => labels.length > 0 ? `helpers.getDescendantsBy(args.current, (node) => ${buildKindFilter(labels)})` : `helpers.getDescendantsBy(args.current, () => true)`,
|
|
12
|
-
HAS_ANCESTORS: (labels) => labels.length > 0 ? `helpers.getAncestorsBy(args.current, (node) => ${buildKindFilter(labels)})` : `helpers.getAncestorsBy(args.current, () => true)`,
|
|
13
|
-
HAS_TYPE: () => `helpers.getType(args.current)`,
|
|
14
|
-
HAS_NAME: () => `args.current.name ? [args.current.name] : []`,
|
|
15
|
-
// --- Direct AST property relationships ---
|
|
16
|
-
HAS_INITIALIZER: () => `args.current.initializer ? [args.current.initializer] : []`,
|
|
17
|
-
HAS_PARAMETERS: () => `args.current.parameters ? [...args.current.parameters] : []`,
|
|
18
|
-
HAS_EXPRESSION: () => `args.current.expression ? [args.current.expression] : []`,
|
|
19
|
-
HAS_TYPE_ANNOTATION: () => `args.current.type ? [args.current.type] : []`,
|
|
20
|
-
HAS_BODY: () => `args.current.body ? [args.current.body] : []`,
|
|
21
|
-
HAS_MEMBERS: () => `args.current.members ? [...args.current.members] : []`,
|
|
22
|
-
HAS_PROPERTIES: () => `args.current.properties ? [...args.current.properties] : []`,
|
|
23
|
-
HAS_ARGUMENTS: () => `args.current.arguments ? [...args.current.arguments] : []`,
|
|
24
|
-
HAS_DECORATORS: () => `args.ts.getDecorators(args.current) || []`,
|
|
25
|
-
// --- Heritage relationships ---
|
|
26
|
-
EXTENDS: () => `(() => { const c = (args.current.heritageClauses || []).find(c => c.token === args.ts.SyntaxKind.ExtendsKeyword); return c ? [...c.types] : []; })()`,
|
|
27
|
-
IMPLEMENTS: () => `(() => { const c = (args.current.heritageClauses || []).find(c => c.token === args.ts.SyntaxKind.ImplementsKeyword); return c ? [...c.types] : []; })()`
|
|
28
|
-
};
|
|
29
|
-
const buildConfigFromQuery = (query) => {
|
|
30
|
-
const nodeExtractors = {};
|
|
31
|
-
const matchStatements = query.statements.filter(
|
|
32
|
-
(s) => s.type === "MatchStatement"
|
|
33
|
-
);
|
|
34
|
-
const variableToLabels = {};
|
|
35
|
-
for (const match of matchStatements) {
|
|
36
|
-
for (const pattern of match.patterns) {
|
|
37
|
-
if (pattern.variable && pattern.labels.length > 0) {
|
|
38
|
-
variableToLabels[pattern.variable] = pattern.labels;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
for (const match of matchStatements) {
|
|
43
|
-
buildExtractorsFromMatch(match, nodeExtractors, variableToLabels);
|
|
44
|
-
}
|
|
45
|
-
return { nodeExtractors };
|
|
46
|
-
};
|
|
47
|
-
const resolveLabels = (pattern, variableToLabels) => {
|
|
48
|
-
if (pattern.labels.length > 0)
|
|
49
|
-
return pattern.labels;
|
|
50
|
-
if (pattern.variable && variableToLabels[pattern.variable]) {
|
|
51
|
-
return variableToLabels[pattern.variable];
|
|
52
|
-
}
|
|
53
|
-
return [];
|
|
54
|
-
};
|
|
55
|
-
const buildExtractorsFromMatch = (match, rootNodeExtractors, variableToLabels) => {
|
|
56
|
-
const { patterns, relationships } = match;
|
|
57
|
-
if (patterns.length === 0)
|
|
58
|
-
return;
|
|
59
|
-
const rootPattern = patterns[0];
|
|
60
|
-
const rootLabels = resolveLabels(rootPattern, variableToLabels);
|
|
61
|
-
for (const label of rootLabels) {
|
|
62
|
-
rootNodeExtractors[label] ??= {
|
|
63
|
-
properties: { ...STANDARD_NODE_PROPERTIES }
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
if (!relationships || relationships.length === 0)
|
|
67
|
-
return;
|
|
68
|
-
let currentExtractors = rootNodeExtractors;
|
|
69
|
-
for (let i = 0; i < relationships.length; i++) {
|
|
70
|
-
const rel = relationships[i];
|
|
71
|
-
const sourcePattern = patterns[i];
|
|
72
|
-
const targetPattern = patterns[i + 1];
|
|
73
|
-
if (!targetPattern)
|
|
74
|
-
break;
|
|
75
|
-
const edgeType = resolveEdgeType(rel.edgeType);
|
|
76
|
-
const codeGenerator = getCodeGenerator(edgeType);
|
|
77
|
-
const sourceLabels = resolveLabels(sourcePattern, variableToLabels);
|
|
78
|
-
const targetLabels = resolveLabels(targetPattern, variableToLabels);
|
|
79
|
-
const targetNodeExtractors = {};
|
|
80
|
-
if (targetLabels.length === 0) {
|
|
81
|
-
targetNodeExtractors["*"] = {
|
|
82
|
-
properties: { ...STANDARD_NODE_PROPERTIES }
|
|
83
|
-
};
|
|
84
|
-
} else {
|
|
85
|
-
for (const label of targetLabels) {
|
|
86
|
-
targetNodeExtractors[label] = {
|
|
87
|
-
properties: { ...STANDARD_NODE_PROPERTIES }
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
for (const sourceLabel of sourceLabels) {
|
|
92
|
-
currentExtractors[sourceLabel] ??= {
|
|
93
|
-
properties: { ...STANDARD_NODE_PROPERTIES }
|
|
94
|
-
};
|
|
95
|
-
currentExtractors[sourceLabel].relationships ??= {};
|
|
96
|
-
currentExtractors[sourceLabel].relationships[edgeType] = {
|
|
97
|
-
extractorCode: codeGenerator(targetLabels),
|
|
98
|
-
nodeExtractors: targetNodeExtractors
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
currentExtractors = targetNodeExtractors;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const resolveEdgeType = (edgeType) => {
|
|
105
|
-
if (!edgeType) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`Relationship type is required in auto-config mode. Supported: ${Object.keys(SUPPORTED_RELATIONSHIPS).join(", ")}`
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
if (Array.isArray(edgeType)) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Union relationship types are not supported in auto-config. Got: ${edgeType.join("|")}`
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
return edgeType;
|
|
116
|
-
};
|
|
117
|
-
const getCodeGenerator = (edgeType) => {
|
|
118
|
-
const generator = SUPPORTED_RELATIONSHIPS[edgeType];
|
|
119
|
-
if (!generator) {
|
|
120
|
-
throw new Error(
|
|
121
|
-
`Unsupported relationship type: "${edgeType}". Auto-config supports: ${Object.keys(SUPPORTED_RELATIONSHIPS).join(", ")}`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
return generator;
|
|
125
|
-
};
|
|
126
|
-
const buildKindFilter = (labels) => {
|
|
127
|
-
return labels.map((l) => `node.kind === args.ts.SyntaxKind.${l}`).join(" || ");
|
|
128
|
-
};
|
|
129
|
-
export {
|
|
130
|
-
buildConfigFromQuery
|
|
131
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
function executeExtractor(code, context) {
|
|
2
|
-
try {
|
|
3
|
-
const fn = new Function(
|
|
4
|
-
...Object.keys(context),
|
|
5
|
-
`
|
|
6
|
-
return ${code}
|
|
7
|
-
`
|
|
8
|
-
);
|
|
9
|
-
return fn(...Object.values(context));
|
|
10
|
-
} catch (error) {
|
|
11
|
-
throw new Error(`Extractor execution failed: ${error.message}`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function extractNodeProperties(context, propertyExectorMap = {}) {
|
|
15
|
-
const properties = {};
|
|
16
|
-
for (const [propertyName, extractorCode] of Object.entries(
|
|
17
|
-
propertyExectorMap
|
|
18
|
-
)) {
|
|
19
|
-
const value = executeExtractor(extractorCode, context);
|
|
20
|
-
properties[propertyName] = value;
|
|
21
|
-
}
|
|
22
|
-
return properties;
|
|
23
|
-
}
|
|
24
|
-
export {
|
|
25
|
-
executeExtractor,
|
|
26
|
-
extractNodeProperties
|
|
27
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./extractor.js";
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import * as ts from "typescript";
|
|
2
|
-
import {
|
|
3
|
-
getAncestorsBy,
|
|
4
|
-
getDescendantsBy,
|
|
5
|
-
getDescendantsByKind,
|
|
6
|
-
getSolutionSourceFiles
|
|
7
|
-
} from "../../codegular/index.js";
|
|
8
|
-
import { processNode } from "./node.processor.js";
|
|
9
|
-
const buildCodeGraph = (program, config) => {
|
|
10
|
-
const codeGraph = {
|
|
11
|
-
nodesById: {},
|
|
12
|
-
edgesBySource: {},
|
|
13
|
-
edgesByTarget: {}
|
|
14
|
-
};
|
|
15
|
-
const sourceFiles = getSolutionSourceFiles(program);
|
|
16
|
-
sourceFiles.forEach((sourceFile) => {
|
|
17
|
-
Object.keys(config.nodeExtractors).forEach((kindName) => {
|
|
18
|
-
const nodes = getDescendantsByKind(sourceFile, ts.SyntaxKind[kindName]);
|
|
19
|
-
nodes.forEach((node) => {
|
|
20
|
-
const extractorContext = {
|
|
21
|
-
args: {
|
|
22
|
-
current: node,
|
|
23
|
-
sourceFile,
|
|
24
|
-
typeChecker: program.getTypeChecker(),
|
|
25
|
-
ts
|
|
26
|
-
},
|
|
27
|
-
helpers: {
|
|
28
|
-
getDescendantsBy,
|
|
29
|
-
getAncestorsBy,
|
|
30
|
-
getType: (node2) => []
|
|
31
|
-
// TODO: add type extraction
|
|
32
|
-
// getType(
|
|
33
|
-
// node,
|
|
34
|
-
// program.getTypeChecker(),
|
|
35
|
-
// new TypeResolutionTracker(),
|
|
36
|
-
// ),
|
|
37
|
-
// getDeclarations: (node: ts.Type | ts.Node) =>
|
|
38
|
-
// getDeclarations(node, context.typeChecker),
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
processNode(extractorContext, config.nodeExtractors, codeGraph);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
return codeGraph;
|
|
46
|
-
};
|
|
47
|
-
export {
|
|
48
|
-
buildCodeGraph
|
|
49
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./code-graph.builder.js";
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { addNode } from "../code.graph.js";
|
|
2
|
-
import {
|
|
3
|
-
extractNodeProperties
|
|
4
|
-
} from "../extractors/index.js";
|
|
5
|
-
import { generateNodeId, getSyntaxKindName } from "../utils.js";
|
|
6
|
-
import { processRelationships } from "./relationship.processor.js";
|
|
7
|
-
const processNode = (context, nodeExtractors, codeGraph) => {
|
|
8
|
-
const nodeId = generateNodeId(context.args.current);
|
|
9
|
-
const kindName = getSyntaxKindName(context.args.current.kind);
|
|
10
|
-
const nodeConfig = nodeExtractors[kindName] ?? nodeExtractors["*"];
|
|
11
|
-
const graphNode = {
|
|
12
|
-
...nodeConfig && nodeConfig.properties ? extractNodeProperties(context, nodeConfig.properties) : {},
|
|
13
|
-
id: nodeId,
|
|
14
|
-
labels: [kindName]
|
|
15
|
-
};
|
|
16
|
-
addNode(codeGraph, graphNode);
|
|
17
|
-
processRelationships(nodeId, context, nodeConfig, codeGraph);
|
|
18
|
-
return graphNode;
|
|
19
|
-
};
|
|
20
|
-
export {
|
|
21
|
-
processNode
|
|
22
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { addEdge } from "../code.graph.js";
|
|
2
|
-
import { executeExtractor } from "../extractors/index.js";
|
|
3
|
-
import { processNode } from "./node.processor.js";
|
|
4
|
-
import { processType } from "./type.processor.js";
|
|
5
|
-
function isTsNode(obj) {
|
|
6
|
-
return obj && "kind" in obj && typeof obj.kind === "number";
|
|
7
|
-
}
|
|
8
|
-
function isTsType(obj) {
|
|
9
|
-
return obj && "flags" in obj && typeof obj.flags === "number" && !("kind" in obj);
|
|
10
|
-
}
|
|
11
|
-
const processRelationships = (parentId, context, nodeConfig, codeGraph) => {
|
|
12
|
-
if (nodeConfig?.relationships) {
|
|
13
|
-
for (const relationshipName of Object.keys(nodeConfig.relationships)) {
|
|
14
|
-
const relationshipExtractorFnCode = nodeConfig.relationships[relationshipName].extractorCode;
|
|
15
|
-
const relationshipNodeExtractors = nodeConfig.relationships[relationshipName].nodeExtractors;
|
|
16
|
-
Object.keys(relationshipNodeExtractors).forEach((label) => {
|
|
17
|
-
const children = executeExtractor(relationshipExtractorFnCode, context);
|
|
18
|
-
children.forEach(
|
|
19
|
-
(child) => {
|
|
20
|
-
if (isTsType(child)) {
|
|
21
|
-
const childNode = processType(
|
|
22
|
-
{ ...context, args: { ...context.args, current: child } },
|
|
23
|
-
relationshipNodeExtractors,
|
|
24
|
-
codeGraph
|
|
25
|
-
);
|
|
26
|
-
addEdge(codeGraph, {
|
|
27
|
-
source: parentId,
|
|
28
|
-
target: childNode.id,
|
|
29
|
-
type: relationshipName
|
|
30
|
-
});
|
|
31
|
-
} else if (isTsNode(child)) {
|
|
32
|
-
const childNode = processNode(
|
|
33
|
-
{ ...context, args: { ...context.args, current: child } },
|
|
34
|
-
relationshipNodeExtractors,
|
|
35
|
-
codeGraph
|
|
36
|
-
);
|
|
37
|
-
addEdge(codeGraph, {
|
|
38
|
-
source: parentId,
|
|
39
|
-
target: childNode.id,
|
|
40
|
-
type: relationshipName
|
|
41
|
-
});
|
|
42
|
-
} else {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`The code provided: ${relationshipExtractorFnCode} returned an invalid child: ${child}. We support only ts.Node and ts.Type array returns from executed code`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
export {
|
|
54
|
-
processRelationships
|
|
55
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { addNode } from "../code.graph.js";
|
|
2
|
-
import { extractNodeProperties } from "../extractors/index.js";
|
|
3
|
-
import { processRelationships } from "./relationship.processor.js";
|
|
4
|
-
const processType = (context, nodeExtractors, codeGraph) => {
|
|
5
|
-
const nodeId = context.args.typeChecker.typeToString(
|
|
6
|
-
context.args.current
|
|
7
|
-
);
|
|
8
|
-
const kindName = "Type";
|
|
9
|
-
const nodeConfig = nodeExtractors[kindName];
|
|
10
|
-
const graphNode = {
|
|
11
|
-
...nodeConfig && nodeConfig.properties ? extractNodeProperties(context, nodeConfig.properties) : {},
|
|
12
|
-
id: nodeId,
|
|
13
|
-
labels: [kindName]
|
|
14
|
-
};
|
|
15
|
-
addNode(codeGraph, graphNode);
|
|
16
|
-
processRelationships(nodeId, context, nodeConfig, codeGraph);
|
|
17
|
-
return graphNode;
|
|
18
|
-
};
|
|
19
|
-
export {
|
|
20
|
-
processType
|
|
21
|
-
};
|
package/code-graph-v2/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { writeFileSync } from "fs";
|
|
2
|
-
import { createProgramFromTsConfig } from "../../codegular/index.js";
|
|
3
|
-
import { buildCodeGraph } from "../graph-builder/index.js";
|
|
4
|
-
async function buildCodeGraphTool(tsConfigPath, config) {
|
|
5
|
-
const program = createProgramFromTsConfig(tsConfigPath);
|
|
6
|
-
const graph = buildCodeGraph(program, config);
|
|
7
|
-
const nodeCount = Object.keys(graph.nodesById).length;
|
|
8
|
-
const edgeCount = Object.values(graph.edgesBySource).flat().length;
|
|
9
|
-
const graphPath = tsConfigPath.replace("tsconfig.json", "code-graph.json");
|
|
10
|
-
writeFileSync(graphPath, JSON.stringify(graph, null, 2));
|
|
11
|
-
return {
|
|
12
|
-
graphPath,
|
|
13
|
-
nodeCount,
|
|
14
|
-
edgeCount
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
buildCodeGraphTool
|
|
19
|
-
};
|
package/code-graph-v2/utils.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
const getSyntaxKindName = (kind) => {
|
|
3
|
-
return ts.SyntaxKind[kind];
|
|
4
|
-
};
|
|
5
|
-
const generateNodeId = (n) => {
|
|
6
|
-
const s = n.getSourceFile();
|
|
7
|
-
const start = s.getLineAndCharacterOfPosition(n.getStart(s));
|
|
8
|
-
const end = s.getLineAndCharacterOfPosition(n.getEnd());
|
|
9
|
-
return `${s.fileName}.${start.line}.${start.character}.${end.line}.${end.character}`;
|
|
10
|
-
};
|
|
11
|
-
const extractFromNodeId = (nodeId) => {
|
|
12
|
-
const parts = nodeId.split(".");
|
|
13
|
-
const endCharacter = parts.pop();
|
|
14
|
-
const endLine = parts.pop();
|
|
15
|
-
const startCharacter = parts.pop();
|
|
16
|
-
const startLine = parts.pop();
|
|
17
|
-
const filePath = parts.join(".");
|
|
18
|
-
return {
|
|
19
|
-
filePath,
|
|
20
|
-
start: {
|
|
21
|
-
line: parseInt(startLine),
|
|
22
|
-
character: parseInt(startCharacter)
|
|
23
|
-
},
|
|
24
|
-
end: {
|
|
25
|
-
line: parseInt(endLine),
|
|
26
|
-
character: parseInt(endCharacter)
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
export {
|
|
31
|
-
extractFromNodeId,
|
|
32
|
-
generateNodeId,
|
|
33
|
-
getSyntaxKindName
|
|
34
|
-
};
|
package/codegular/index.js
DELETED
package/codegular/node.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import * as ts from "typescript";
|
|
2
|
-
import { getNameByIdentifier } from "./string.js";
|
|
3
|
-
const { forEachChild, SyntaxKind } = ts;
|
|
4
|
-
const hasKind = (kinds) => (child) => kinds.includes(child.kind);
|
|
5
|
-
const hasName = (names) => (child) => names.includes(getNameByIdentifier(child));
|
|
6
|
-
const hasDecorator = (decoratorName) => (child) => findByDecorator(child, decoratorName) !== void 0;
|
|
7
|
-
const hasAnyDecoratorName = (node, decoratorNames) => {
|
|
8
|
-
const decorators = getChildrenBy(node, hasKind([SyntaxKind.Decorator]));
|
|
9
|
-
return decorators.find((d) => {
|
|
10
|
-
return getChildrenBy(d, hasKind([SyntaxKind.CallExpression])).find(
|
|
11
|
-
(callExpr) => {
|
|
12
|
-
return decoratorNames.includes(getNameByIdentifier(callExpr));
|
|
13
|
-
}
|
|
14
|
-
) !== void 0;
|
|
15
|
-
}) !== void 0;
|
|
16
|
-
};
|
|
17
|
-
const every = () => true;
|
|
18
|
-
function getChildrenBy(node, findFunc = every) {
|
|
19
|
-
const elements = [];
|
|
20
|
-
forEachChild(node, (child) => {
|
|
21
|
-
if (!findFunc(child)) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
elements.push(child);
|
|
25
|
-
});
|
|
26
|
-
return elements;
|
|
27
|
-
}
|
|
28
|
-
function findChildOrThrow(node, findFunc) {
|
|
29
|
-
let element;
|
|
30
|
-
forEachChild(node, (child) => {
|
|
31
|
-
if (element)
|
|
32
|
-
return;
|
|
33
|
-
if (findFunc(child)) {
|
|
34
|
-
element = child;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
if (element === void 0) {
|
|
39
|
-
throw new Error("Could not find child");
|
|
40
|
-
}
|
|
41
|
-
return element;
|
|
42
|
-
}
|
|
43
|
-
const getDecoratorName = (d) => {
|
|
44
|
-
return getNameByIdentifier(
|
|
45
|
-
getChildrenBy(d, hasKind([SyntaxKind.CallExpression]))[0]
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
const findByDecorator = (node, decoratorName) => {
|
|
49
|
-
const decorators = getChildrenBy(node, hasKind([SyntaxKind.Decorator]));
|
|
50
|
-
if (decorators.length === 0) {
|
|
51
|
-
return void 0;
|
|
52
|
-
}
|
|
53
|
-
return decorators.find((d) => {
|
|
54
|
-
return getChildrenBy(d, hasKind([SyntaxKind.CallExpression])).find(
|
|
55
|
-
(callExpr) => {
|
|
56
|
-
return getNameByIdentifier(callExpr) === decoratorName;
|
|
57
|
-
}
|
|
58
|
-
) !== void 0;
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
export {
|
|
62
|
-
every,
|
|
63
|
-
findByDecorator,
|
|
64
|
-
findChildOrThrow,
|
|
65
|
-
getChildrenBy,
|
|
66
|
-
getDecoratorName,
|
|
67
|
-
hasAnyDecoratorName,
|
|
68
|
-
hasDecorator,
|
|
69
|
-
hasKind,
|
|
70
|
-
hasName
|
|
71
|
-
};
|
package/codegular/program.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import * as path from "path";
|
|
2
|
-
import * as ts from "typescript";
|
|
3
|
-
const createSourceFileFromContent = (fileName, content) => {
|
|
4
|
-
return ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
|
5
|
-
};
|
|
6
|
-
const createProgramFromFileSystem = (fileSystem) => {
|
|
7
|
-
const compilerOptions = {
|
|
8
|
-
target: ts.ScriptTarget.ES2020,
|
|
9
|
-
module: ts.ModuleKind.CommonJS,
|
|
10
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
11
|
-
allowSyntheticDefaultImports: true,
|
|
12
|
-
esModuleInterop: true,
|
|
13
|
-
skipLibCheck: true,
|
|
14
|
-
lib: ["ES2020", "DOM"]
|
|
15
|
-
};
|
|
16
|
-
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
17
|
-
const originalGetSourceFile = compilerHost.getSourceFile;
|
|
18
|
-
compilerHost.getSourceFile = (fileName, languageVersion) => {
|
|
19
|
-
if (fileSystem[fileName]) {
|
|
20
|
-
return ts.createSourceFile(
|
|
21
|
-
fileName,
|
|
22
|
-
fileSystem[fileName],
|
|
23
|
-
languageVersion,
|
|
24
|
-
true
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
return originalGetSourceFile(fileName, languageVersion);
|
|
28
|
-
};
|
|
29
|
-
const fileNames = Object.keys(fileSystem);
|
|
30
|
-
return ts.createProgram(fileNames, compilerOptions, compilerHost);
|
|
31
|
-
};
|
|
32
|
-
const createHybridProgram = (generatedFiles, options) => {
|
|
33
|
-
const rootPath = path.resolve(options?.workspaceRoot ?? process.cwd());
|
|
34
|
-
const fileSystem = {};
|
|
35
|
-
for (const { filePath, code } of generatedFiles) {
|
|
36
|
-
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(rootPath, filePath);
|
|
37
|
-
fileSystem[absolutePath] = code;
|
|
38
|
-
}
|
|
39
|
-
const rootNames = Object.keys(fileSystem).filter(
|
|
40
|
-
(p) => p.endsWith(".ts")
|
|
41
|
-
);
|
|
42
|
-
const compilerOptions = {
|
|
43
|
-
target: ts.ScriptTarget.ES2020,
|
|
44
|
-
module: ts.ModuleKind.ESNext,
|
|
45
|
-
moduleResolution: ts.ModuleResolutionKind.Node10,
|
|
46
|
-
allowSyntheticDefaultImports: true,
|
|
47
|
-
esModuleInterop: true,
|
|
48
|
-
skipLibCheck: true,
|
|
49
|
-
lib: ["ES2020", "DOM"],
|
|
50
|
-
strict: true
|
|
51
|
-
};
|
|
52
|
-
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
53
|
-
const originalGetSourceFile = compilerHost.getSourceFile;
|
|
54
|
-
compilerHost.getCurrentDirectory = () => rootPath;
|
|
55
|
-
compilerHost.getSourceFile = (fileName, languageVersion) => {
|
|
56
|
-
const normalized = path.normalize(fileName);
|
|
57
|
-
const content = fileSystem[normalized] ?? fileSystem[fileName] ?? fileSystem[path.resolve(rootPath, fileName)];
|
|
58
|
-
if (content !== void 0) {
|
|
59
|
-
return ts.createSourceFile(
|
|
60
|
-
fileName,
|
|
61
|
-
content,
|
|
62
|
-
languageVersion,
|
|
63
|
-
true
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
return originalGetSourceFile.call(
|
|
67
|
-
compilerHost,
|
|
68
|
-
fileName,
|
|
69
|
-
languageVersion
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
return ts.createProgram(rootNames, compilerOptions, compilerHost);
|
|
73
|
-
};
|
|
74
|
-
const createProgramFromTsConfig = (configPath) => {
|
|
75
|
-
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
76
|
-
if (configFile.error) {
|
|
77
|
-
const errorMessage = ts.formatDiagnostic(configFile.error, {
|
|
78
|
-
getCurrentDirectory: () => process.cwd(),
|
|
79
|
-
getNewLine: () => "\n",
|
|
80
|
-
getCanonicalFileName: (fileName) => fileName
|
|
81
|
-
});
|
|
82
|
-
throw new Error(`Error reading tsconfig at ${configPath}: ${errorMessage}`);
|
|
83
|
-
}
|
|
84
|
-
const parsedConfig = ts.parseJsonConfigFileContent(
|
|
85
|
-
configFile.config,
|
|
86
|
-
ts.sys,
|
|
87
|
-
path.dirname(configPath)
|
|
88
|
-
);
|
|
89
|
-
return ts.createProgram(
|
|
90
|
-
parsedConfig.fileNames,
|
|
91
|
-
parsedConfig.options,
|
|
92
|
-
ts.createCompilerHost(parsedConfig.options)
|
|
93
|
-
);
|
|
94
|
-
};
|
|
95
|
-
export {
|
|
96
|
-
createHybridProgram,
|
|
97
|
-
createProgramFromFileSystem,
|
|
98
|
-
createProgramFromTsConfig,
|
|
99
|
-
createSourceFileFromContent
|
|
100
|
-
};
|