@living-architecture/riviere-extract-ts 0.2.2 → 0.2.4
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/dist/domain/connection-detection/call-graph/build-call-graph.d.ts +7 -0
- package/dist/domain/connection-detection/call-graph/build-call-graph.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/build-call-graph.js +77 -0
- package/dist/domain/connection-detection/call-graph/call-graph-fixtures.d.ts +8 -0
- package/dist/domain/connection-detection/call-graph/call-graph-fixtures.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/call-graph-fixtures.js +35 -0
- package/dist/domain/connection-detection/call-graph/call-graph-shared.d.ts +17 -0
- package/dist/domain/connection-detection/call-graph/call-graph-shared.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/call-graph-shared.js +46 -0
- package/dist/domain/connection-detection/call-graph/call-graph-types.d.ts +23 -0
- package/dist/domain/connection-detection/call-graph/call-graph-types.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/call-graph-types.js +10 -0
- package/dist/domain/connection-detection/call-graph/deduplicate-links.d.ts +4 -0
- package/dist/domain/connection-detection/call-graph/deduplicate-links.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/deduplicate-links.js +53 -0
- package/dist/domain/connection-detection/call-graph/trace-calls.d.ts +7 -0
- package/dist/domain/connection-detection/call-graph/trace-calls.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/trace-calls.js +51 -0
- package/dist/domain/connection-detection/call-graph/type-resolver-fixtures.d.ts +10 -0
- package/dist/domain/connection-detection/call-graph/type-resolver-fixtures.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/type-resolver-fixtures.js +51 -0
- package/dist/domain/connection-detection/call-graph/type-resolver.d.ts +15 -0
- package/dist/domain/connection-detection/call-graph/type-resolver.d.ts.map +1 -0
- package/dist/domain/connection-detection/call-graph/type-resolver.js +128 -0
- package/dist/domain/connection-detection/component-index.d.ts +10 -0
- package/dist/domain/connection-detection/component-index.d.ts.map +1 -0
- package/dist/domain/connection-detection/component-index.js +27 -0
- package/dist/domain/connection-detection/connection-detection-error.d.ts +13 -0
- package/dist/domain/connection-detection/connection-detection-error.d.ts.map +1 -0
- package/dist/domain/connection-detection/connection-detection-error.js +14 -0
- package/dist/domain/connection-detection/detect-connections.d.ts +10 -0
- package/dist/domain/connection-detection/detect-connections.d.ts.map +1 -0
- package/dist/domain/connection-detection/detect-connections.js +16 -0
- package/dist/domain/connection-detection/extracted-link.d.ts +5 -0
- package/dist/domain/connection-detection/extracted-link.d.ts.map +1 -0
- package/dist/domain/connection-detection/extracted-link.js +1 -0
- package/dist/domain/connection-detection/interface-resolution/resolve-interface.d.ts +16 -0
- package/dist/domain/connection-detection/interface-resolution/resolve-interface.d.ts.map +1 -0
- package/dist/domain/connection-detection/interface-resolution/resolve-interface.js +79 -0
- package/dist/domain/value-extraction/enrich-components.d.ts.map +1 -1
- package/dist/domain/value-extraction/enrich-components.js +46 -1
- package/dist/shell/index.d.ts +4 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +3 -0
- package/package.json +3 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Project } from 'ts-morph';
|
|
2
|
+
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
3
|
+
import type { ComponentIndex } from '../component-index';
|
|
4
|
+
import type { ExtractedLink } from '../extracted-link';
|
|
5
|
+
import type { CallGraphOptions } from './call-graph-types';
|
|
6
|
+
export declare function buildCallGraph(project: Project, components: readonly EnrichedComponent[], componentIndex: ComponentIndex, options: CallGraphOptions): ExtractedLink[];
|
|
7
|
+
//# sourceMappingURL=build-call-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-call-graph.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/build-call-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EAEb,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EACV,gBAAgB,EACjB,MAAM,oBAAoB,CAAA;AAyG3B,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,SAAS,iBAAiB,EAAE,EACxC,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,gBAAgB,GACxB,aAAa,EAAE,CAgBjB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Node, SyntaxKind, } from 'ts-morph';
|
|
2
|
+
import { componentIdentity } from './call-graph-types';
|
|
3
|
+
import { findClassInProject, traceCallsInBody } from './trace-calls';
|
|
4
|
+
import { deduplicateLinks } from './deduplicate-links';
|
|
5
|
+
import { resolveCallExpressionReceiverType } from './type-resolver';
|
|
6
|
+
import { getCalledMethodName, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
7
|
+
function processCallExpression(callExpr, component, methodName, project, componentIndex, rawLinks, uncertainLinks, options) {
|
|
8
|
+
if (!Node.isPropertyAccessExpression(callExpr.getExpression())) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const sourceFile = callExpr.getSourceFile();
|
|
12
|
+
const typeResult = resolveCallExpressionReceiverType(callExpr, sourceFile, { strict: options.strict, });
|
|
13
|
+
const currentCallSite = {
|
|
14
|
+
filePath: component.location.file,
|
|
15
|
+
lineNumber: callExpr.getStartLineNumber(),
|
|
16
|
+
methodName,
|
|
17
|
+
};
|
|
18
|
+
if (!typeResult.resolved) {
|
|
19
|
+
uncertainLinks.push({
|
|
20
|
+
source: component,
|
|
21
|
+
reason: typeResult.reason,
|
|
22
|
+
callSite: currentCallSite,
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const typeName = typeResult.typeName;
|
|
27
|
+
const calledMethodName = getCalledMethodName(callExpr);
|
|
28
|
+
const { component: targetComponent, resolvedTypeName, uncertain, } = resolveTypeThroughInterface(typeName, project, componentIndex, options);
|
|
29
|
+
if (targetComponent !== undefined) {
|
|
30
|
+
if (componentIdentity(component) !== componentIdentity(targetComponent)) {
|
|
31
|
+
rawLinks.push({
|
|
32
|
+
source: component,
|
|
33
|
+
target: targetComponent,
|
|
34
|
+
callSite: currentCallSite,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
traceNonComponent(project, componentIndex, component, resolvedTypeName ?? typeName, calledMethodName, currentCallSite, rawLinks, uncertainLinks, uncertain, options);
|
|
40
|
+
}
|
|
41
|
+
function processMethod(method, component, project, componentIndex, rawLinks, uncertainLinks, options) {
|
|
42
|
+
const methodName = method.getName();
|
|
43
|
+
const callExpressions = method.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
44
|
+
for (const callExpr of callExpressions) {
|
|
45
|
+
processCallExpression(callExpr, component, methodName, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function buildCallGraph(project, components, componentIndex, options) {
|
|
49
|
+
const rawLinks = [];
|
|
50
|
+
const uncertainLinks = [];
|
|
51
|
+
for (const component of components) {
|
|
52
|
+
const classDecl = findClassInProject(project, component);
|
|
53
|
+
if (classDecl === undefined) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
for (const method of classDecl.getMethods()) {
|
|
57
|
+
processMethod(method, component, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return deduplicateLinks(rawLinks, uncertainLinks);
|
|
61
|
+
}
|
|
62
|
+
function traceNonComponent(project, componentIndex, source, typeName, calledMethodName, callSite, rawLinks, uncertainLinks, interfaceUncertainty, options) {
|
|
63
|
+
const visited = new Set();
|
|
64
|
+
visited.add(`${typeName}.${calledMethodName}`);
|
|
65
|
+
const { method, classFound } = findMethodInProject(project, typeName, calledMethodName);
|
|
66
|
+
if (method !== undefined) {
|
|
67
|
+
traceCallsInBody(method, project, componentIndex, source, callSite, visited, rawLinks, uncertainLinks, options);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!classFound && interfaceUncertainty !== undefined) {
|
|
71
|
+
uncertainLinks.push({
|
|
72
|
+
source,
|
|
73
|
+
reason: interfaceUncertainty,
|
|
74
|
+
callSite,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
3
|
+
import type { CallGraphOptions } from './call-graph-types';
|
|
4
|
+
export declare const sharedProject: Project;
|
|
5
|
+
export declare function nextFile(content: string): string;
|
|
6
|
+
export declare function buildComponent(name: string, file: string, line: number, overrides?: Partial<EnrichedComponent>): EnrichedComponent;
|
|
7
|
+
export declare function defaultOptions(): CallGraphOptions;
|
|
8
|
+
//# sourceMappingURL=call-graph-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-graph-fixtures.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/call-graph-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,eAAO,MAAM,aAAa,SAOxB,CAAA;AAIF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACzC,iBAAiB,CAYnB;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAKjD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
export const sharedProject = new Project({
|
|
3
|
+
useInMemoryFileSystem: true,
|
|
4
|
+
compilerOptions: {
|
|
5
|
+
strict: true,
|
|
6
|
+
target: 99,
|
|
7
|
+
module: 99,
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
const counter = { value: 0 };
|
|
11
|
+
export function nextFile(content) {
|
|
12
|
+
counter.value++;
|
|
13
|
+
const filePath = `/src/test-call-graph-${counter.value}.ts`;
|
|
14
|
+
sharedProject.createSourceFile(filePath, content);
|
|
15
|
+
return filePath;
|
|
16
|
+
}
|
|
17
|
+
export function buildComponent(name, file, line, overrides = {}) {
|
|
18
|
+
return {
|
|
19
|
+
type: 'useCase',
|
|
20
|
+
name,
|
|
21
|
+
location: {
|
|
22
|
+
file,
|
|
23
|
+
line,
|
|
24
|
+
},
|
|
25
|
+
domain: 'orders',
|
|
26
|
+
metadata: {},
|
|
27
|
+
...overrides,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function defaultOptions() {
|
|
31
|
+
return {
|
|
32
|
+
strict: false,
|
|
33
|
+
sourceFilePaths: sharedProject.getSourceFiles().map((sf) => sf.getFilePath()),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CallExpression, type MethodDeclaration, type Project } from 'ts-morph';
|
|
2
|
+
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
3
|
+
import type { ComponentIndex } from '../component-index';
|
|
4
|
+
import type { CallGraphOptions } from './call-graph-types';
|
|
5
|
+
export interface InterfaceResolutionOutcome {
|
|
6
|
+
component: EnrichedComponent | undefined;
|
|
7
|
+
resolvedTypeName: string | undefined;
|
|
8
|
+
uncertain: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface MethodLookup {
|
|
11
|
+
method: MethodDeclaration | undefined;
|
|
12
|
+
classFound: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function getCalledMethodName(callExpr: CallExpression): string;
|
|
15
|
+
export declare function resolveTypeThroughInterface(typeName: string, project: Project, componentIndex: ComponentIndex, options: CallGraphOptions): InterfaceResolutionOutcome;
|
|
16
|
+
export declare function findMethodInProject(project: Project, typeName: string, methodName: string): MethodLookup;
|
|
17
|
+
//# sourceMappingURL=call-graph-shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-graph-shared.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/call-graph-shared.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,OAAO,EAC1D,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAG1D,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,iBAAiB,GAAG,SAAS,CAAA;IACxC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAA;IACrC,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAGpE;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,gBAAgB,GACxB,0BAA0B,CAwB5B;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,CAgBd"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { resolveInterface } from '../interface-resolution/resolve-interface';
|
|
3
|
+
export function getCalledMethodName(callExpr) {
|
|
4
|
+
const expression = callExpr.getExpression();
|
|
5
|
+
return expression.asKindOrThrow(SyntaxKind.PropertyAccessExpression).getName();
|
|
6
|
+
}
|
|
7
|
+
export function resolveTypeThroughInterface(typeName, project, componentIndex, options) {
|
|
8
|
+
const component = componentIndex.getComponentByTypeName(typeName);
|
|
9
|
+
if (component !== undefined) {
|
|
10
|
+
return {
|
|
11
|
+
component,
|
|
12
|
+
resolvedTypeName: undefined,
|
|
13
|
+
uncertain: undefined,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const interfaceResult = resolveInterface(typeName, project, options.sourceFilePaths, { strict: options.strict, });
|
|
17
|
+
if (interfaceResult.resolved) {
|
|
18
|
+
return {
|
|
19
|
+
component: componentIndex.getComponentByTypeName(interfaceResult.typeName),
|
|
20
|
+
resolvedTypeName: interfaceResult.typeName,
|
|
21
|
+
uncertain: undefined,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
component: undefined,
|
|
26
|
+
resolvedTypeName: undefined,
|
|
27
|
+
uncertain: interfaceResult.typeDefinedInSource ? interfaceResult.reason : undefined,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function findMethodInProject(project, typeName, methodName) {
|
|
31
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
32
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
33
|
+
if (classDecl.getName() !== typeName) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
method: classDecl.getMethod(methodName),
|
|
38
|
+
classFound: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
method: undefined,
|
|
44
|
+
classFound: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
2
|
+
export interface CallGraphOptions {
|
|
3
|
+
strict: boolean;
|
|
4
|
+
sourceFilePaths: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface CallSite {
|
|
7
|
+
filePath: string;
|
|
8
|
+
lineNumber: number;
|
|
9
|
+
methodName: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RawLink {
|
|
12
|
+
source: EnrichedComponent;
|
|
13
|
+
target: EnrichedComponent;
|
|
14
|
+
callSite: CallSite;
|
|
15
|
+
}
|
|
16
|
+
export interface UncertainRawLink {
|
|
17
|
+
source: EnrichedComponent;
|
|
18
|
+
reason: string;
|
|
19
|
+
callSite: CallSite;
|
|
20
|
+
}
|
|
21
|
+
export declare function componentIdentity(component: EnrichedComponent): string;
|
|
22
|
+
export declare function stripGenericArgs(typeName: string): string;
|
|
23
|
+
//# sourceMappingURL=call-graph-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-graph-types.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/call-graph-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,CAEtE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function componentIdentity(component) {
|
|
2
|
+
return `${component.domain}:${component.type}:${component.name}`;
|
|
3
|
+
}
|
|
4
|
+
export function stripGenericArgs(typeName) {
|
|
5
|
+
const index = typeName.indexOf('<');
|
|
6
|
+
if (index === -1) {
|
|
7
|
+
return typeName;
|
|
8
|
+
}
|
|
9
|
+
return typeName.slice(0, index);
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExtractedLink } from '../extracted-link';
|
|
2
|
+
import type { RawLink, UncertainRawLink } from './call-graph-types';
|
|
3
|
+
export declare function deduplicateLinks(rawLinks: RawLink[], uncertainLinks: UncertainRawLink[], repository?: string): ExtractedLink[];
|
|
4
|
+
//# sourceMappingURL=deduplicate-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deduplicate-links.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/deduplicate-links.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EACV,OAAO,EAAE,gBAAgB,EAC1B,MAAM,oBAAoB,CAAA;AAwD3B,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,cAAc,EAAE,gBAAgB,EAAE,EAClC,UAAU,SAAK,GACd,aAAa,EAAE,CAkCjB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { componentIdentity } from './call-graph-types';
|
|
2
|
+
function linkKey(source, target, type) {
|
|
3
|
+
return `${source}|${target}|${type}`;
|
|
4
|
+
}
|
|
5
|
+
function buildExtractedLink(source, target, type, callSite, repository) {
|
|
6
|
+
return {
|
|
7
|
+
source,
|
|
8
|
+
target,
|
|
9
|
+
type,
|
|
10
|
+
sourceLocation: {
|
|
11
|
+
repository,
|
|
12
|
+
filePath: callSite.filePath,
|
|
13
|
+
lineNumber: callSite.lineNumber,
|
|
14
|
+
methodName: callSite.methodName,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function buildUncertainLink(source, reason, callSite, repository) {
|
|
19
|
+
return {
|
|
20
|
+
source,
|
|
21
|
+
target: '_unresolved',
|
|
22
|
+
type: 'sync',
|
|
23
|
+
_uncertain: reason,
|
|
24
|
+
sourceLocation: {
|
|
25
|
+
repository,
|
|
26
|
+
filePath: callSite.filePath,
|
|
27
|
+
lineNumber: callSite.lineNumber,
|
|
28
|
+
methodName: callSite.methodName,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function deduplicateLinks(rawLinks, uncertainLinks, repository = '') {
|
|
33
|
+
const seen = new Map();
|
|
34
|
+
for (const raw of rawLinks) {
|
|
35
|
+
const sourceId = componentIdentity(raw.source);
|
|
36
|
+
const targetId = componentIdentity(raw.target);
|
|
37
|
+
const type = 'sync';
|
|
38
|
+
const key = linkKey(sourceId, targetId, type);
|
|
39
|
+
const existing = seen.get(key);
|
|
40
|
+
if (existing !== undefined) {
|
|
41
|
+
if (raw.callSite.lineNumber < existing.sourceLocation.lineNumber) {
|
|
42
|
+
seen.set(key, buildExtractedLink(sourceId, targetId, type, raw.callSite, repository));
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
seen.set(key, buildExtractedLink(sourceId, targetId, type, raw.callSite, repository));
|
|
47
|
+
}
|
|
48
|
+
const result = [...seen.values()];
|
|
49
|
+
for (const uncertain of uncertainLinks) {
|
|
50
|
+
result.push(buildUncertainLink(componentIdentity(uncertain.source), uncertain.reason, uncertain.callSite, repository));
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ClassDeclaration, type Project, type MethodDeclaration } from 'ts-morph';
|
|
2
|
+
import type { ComponentIndex } from '../component-index';
|
|
3
|
+
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
4
|
+
import type { CallGraphOptions, CallSite, RawLink, UncertainRawLink } from './call-graph-types';
|
|
5
|
+
export declare function traceCallsInBody(body: MethodDeclaration, project: Project, componentIndex: ComponentIndex, sourceComponent: EnrichedComponent, originCallSite: CallSite, visited: Set<string>, results: RawLink[], uncertainResults: UncertainRawLink[], options: CallGraphOptions): void;
|
|
6
|
+
export declare function findClassInProject(project: Project, component: EnrichedComponent): ClassDeclaration | undefined;
|
|
7
|
+
//# sourceMappingURL=trace-calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-calls.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/trace-calls.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAE,KAAK,iBAAiB,EAC5D,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,KAAK,EACV,gBAAgB,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EACtD,MAAM,oBAAoB,CAAA;AAS3B,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,eAAe,EAAE,iBAAiB,EAClC,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EACpB,OAAO,EAAE,OAAO,EAAE,EAClB,gBAAgB,EAAE,gBAAgB,EAAE,EACpC,OAAO,EAAE,gBAAgB,GACxB,IAAI,CAiEN;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,iBAAiB,GAC3B,gBAAgB,GAAG,SAAS,CAM9B"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { componentIdentity } from './call-graph-types';
|
|
3
|
+
import { resolveCallExpressionReceiverType } from './type-resolver';
|
|
4
|
+
import { getCalledMethodName, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
5
|
+
export function traceCallsInBody(body, project, componentIndex, sourceComponent, originCallSite, visited, results, uncertainResults, options) {
|
|
6
|
+
const callExpressions = body.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
7
|
+
for (const callExpr of callExpressions) {
|
|
8
|
+
const sourceFile = callExpr.getSourceFile();
|
|
9
|
+
const typeResult = resolveCallExpressionReceiverType(callExpr, sourceFile, { strict: options.strict, });
|
|
10
|
+
if (!typeResult.resolved) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const typeName = typeResult.typeName;
|
|
14
|
+
const calledMethodName = getCalledMethodName(callExpr);
|
|
15
|
+
const { component: targetComponent, resolvedTypeName, uncertain, } = resolveTypeThroughInterface(typeName, project, componentIndex, options);
|
|
16
|
+
if (targetComponent !== undefined) {
|
|
17
|
+
if (componentIdentity(sourceComponent) !== componentIdentity(targetComponent)) {
|
|
18
|
+
results.push({
|
|
19
|
+
source: sourceComponent,
|
|
20
|
+
target: targetComponent,
|
|
21
|
+
callSite: originCallSite,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const traceTypeName = resolvedTypeName ?? typeName;
|
|
27
|
+
const visitKey = `${traceTypeName}.${calledMethodName}`;
|
|
28
|
+
if (visited.has(visitKey)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
visited.add(visitKey);
|
|
32
|
+
const { method: resolvedMethod, classFound } = findMethodInProject(project, traceTypeName, calledMethodName);
|
|
33
|
+
if (resolvedMethod !== undefined) {
|
|
34
|
+
traceCallsInBody(resolvedMethod, project, componentIndex, sourceComponent, originCallSite, visited, results, uncertainResults, options);
|
|
35
|
+
}
|
|
36
|
+
else if (!classFound && uncertain !== undefined) {
|
|
37
|
+
uncertainResults.push({
|
|
38
|
+
source: sourceComponent,
|
|
39
|
+
reason: uncertain,
|
|
40
|
+
callSite: originCallSite,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function findClassInProject(project, component) {
|
|
46
|
+
const sourceFile = project.getSourceFile(component.location.file);
|
|
47
|
+
if (sourceFile === undefined) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return sourceFile.getClasses().find((c) => c.getStartLineNumber() === component.location.line);
|
|
51
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Project, CallExpression } from 'ts-morph';
|
|
2
|
+
export declare class CallExpressionNotFoundError extends Error {
|
|
3
|
+
constructor(scope: string, method: string);
|
|
4
|
+
}
|
|
5
|
+
export declare const sharedProject: Project;
|
|
6
|
+
export declare function nextFile(content: string): string;
|
|
7
|
+
export declare function getFirstCallExpression(filePath: string, className: string, methodName: string): CallExpression;
|
|
8
|
+
export declare function getCallExpressionFromFunction(filePath: string, functionName: string): CallExpression;
|
|
9
|
+
export declare function getCallExpressionByText(filePath: string, className: string, methodName: string, text: string): CallExpression;
|
|
10
|
+
//# sourceMappingURL=type-resolver-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-resolver-fixtures.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/type-resolver-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAAE,cAAc,EACxB,MAAM,UAAU,CAAA;AAEjB,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI1C;AAED,eAAO,MAAM,aAAa,SAOxB,CAAA;AAGF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,cAAc,CAQhB;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,cAAc,CAOhB;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,cAAc,CAQhB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Project, CallExpression, SyntaxKind } from 'ts-morph';
|
|
2
|
+
export class CallExpressionNotFoundError extends Error {
|
|
3
|
+
constructor(scope, method) {
|
|
4
|
+
super(`No call expression found in ${scope}.${method}`);
|
|
5
|
+
this.name = 'CallExpressionNotFoundError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export const sharedProject = new Project({
|
|
9
|
+
useInMemoryFileSystem: true,
|
|
10
|
+
compilerOptions: {
|
|
11
|
+
strict: true,
|
|
12
|
+
target: 99,
|
|
13
|
+
module: 99,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
const counter = { value: 0 };
|
|
17
|
+
export function nextFile(content) {
|
|
18
|
+
counter.value++;
|
|
19
|
+
const filePath = `/src/test-type-resolver-${counter.value}.ts`;
|
|
20
|
+
sharedProject.createSourceFile(filePath, content);
|
|
21
|
+
return filePath;
|
|
22
|
+
}
|
|
23
|
+
export function getFirstCallExpression(filePath, className, methodName) {
|
|
24
|
+
const sourceFile = sharedProject.getSourceFileOrThrow(filePath);
|
|
25
|
+
const classDecl = sourceFile.getClassOrThrow(className);
|
|
26
|
+
const method = classDecl.getMethodOrThrow(methodName);
|
|
27
|
+
const callExprs = method.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
28
|
+
const [first] = callExprs;
|
|
29
|
+
if (!first)
|
|
30
|
+
throw new CallExpressionNotFoundError(className, methodName);
|
|
31
|
+
return first;
|
|
32
|
+
}
|
|
33
|
+
export function getCallExpressionFromFunction(filePath, functionName) {
|
|
34
|
+
const sourceFile = sharedProject.getSourceFileOrThrow(filePath);
|
|
35
|
+
const funcDecl = sourceFile.getFunctionOrThrow(functionName);
|
|
36
|
+
const callExprs = funcDecl.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
37
|
+
const [first] = callExprs;
|
|
38
|
+
if (!first)
|
|
39
|
+
throw new CallExpressionNotFoundError('function', functionName);
|
|
40
|
+
return first;
|
|
41
|
+
}
|
|
42
|
+
export function getCallExpressionByText(filePath, className, methodName, text) {
|
|
43
|
+
const sourceFile = sharedProject.getSourceFileOrThrow(filePath);
|
|
44
|
+
const classDecl = sourceFile.getClassOrThrow(className);
|
|
45
|
+
const method = classDecl.getMethodOrThrow(methodName);
|
|
46
|
+
const callExprs = method.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
47
|
+
const match = callExprs.find((c) => c.getText().includes(text));
|
|
48
|
+
if (!match)
|
|
49
|
+
throw new CallExpressionNotFoundError(className, methodName);
|
|
50
|
+
return match;
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CallExpression, SourceFile } from 'ts-morph';
|
|
2
|
+
interface TypeResolutionSuccess {
|
|
3
|
+
resolved: true;
|
|
4
|
+
typeName: string;
|
|
5
|
+
}
|
|
6
|
+
interface TypeResolutionUncertain {
|
|
7
|
+
resolved: false;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
export type TypeResolution = TypeResolutionSuccess | TypeResolutionUncertain;
|
|
11
|
+
export declare function resolveCallExpressionReceiverType(callExpression: CallExpression, sourceFile: SourceFile, options: {
|
|
12
|
+
strict: boolean;
|
|
13
|
+
}): TypeResolution;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=type-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-resolver.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/type-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAAE,UAAU,EAC3B,MAAM,UAAU,CAAA;AAIjB,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,IAAI,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,cAAc,GAAG,qBAAqB,GAAG,uBAAuB,CAAA;AA2H5E,wBAAgB,iCAAiC,CAC/C,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC3B,cAAc,CAwBhB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CallExpression, SourceFile, Node } from 'ts-morph';
|
|
2
|
+
import { ConnectionDetectionError } from '../connection-detection-error';
|
|
3
|
+
function stripGenerics(typeName) {
|
|
4
|
+
const angleBracketIndex = typeName.indexOf('<');
|
|
5
|
+
if (angleBracketIndex === -1)
|
|
6
|
+
return typeName;
|
|
7
|
+
return typeName.slice(0, angleBracketIndex);
|
|
8
|
+
}
|
|
9
|
+
function stripPromise(typeName) {
|
|
10
|
+
if (typeName.startsWith('Promise<') && typeName.endsWith('>')) {
|
|
11
|
+
return typeName.slice(8, -1);
|
|
12
|
+
}
|
|
13
|
+
return typeName;
|
|
14
|
+
}
|
|
15
|
+
const UNRESOLVABLE_TYPES = new Set(['any', 'unknown', 'object']);
|
|
16
|
+
function isUnresolvableType(typeName) {
|
|
17
|
+
return UNRESOLVABLE_TYPES.has(typeName);
|
|
18
|
+
}
|
|
19
|
+
function getReceiverExpression(callExpression) {
|
|
20
|
+
const expression = callExpression.getExpression();
|
|
21
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
22
|
+
return expression.getExpression();
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
function extractTypeNameFromType(type) {
|
|
27
|
+
const symbol = type.getSymbol() ?? type.getAliasSymbol();
|
|
28
|
+
return symbol?.getName() ?? type.getText();
|
|
29
|
+
}
|
|
30
|
+
function resolveCallExpressionReturnType(callExpr) {
|
|
31
|
+
const expr = callExpr.getExpression();
|
|
32
|
+
if (!Node.isPropertyAccessExpression(expr))
|
|
33
|
+
return undefined;
|
|
34
|
+
const methodName = expr.getName();
|
|
35
|
+
const receiverType = expr.getExpression().getType();
|
|
36
|
+
const receiverSymbol = receiverType.getSymbol();
|
|
37
|
+
if (!receiverSymbol)
|
|
38
|
+
return undefined;
|
|
39
|
+
for (const decl of receiverSymbol.getDeclarations()) {
|
|
40
|
+
if (!Node.isClassDeclaration(decl))
|
|
41
|
+
continue;
|
|
42
|
+
const method = decl.getMethod(methodName);
|
|
43
|
+
if (!method)
|
|
44
|
+
continue;
|
|
45
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
46
|
+
if (returnTypeNode) {
|
|
47
|
+
return stripPromise(returnTypeNode.getText());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function resolveAwaitedCallReturnType(initializer) {
|
|
53
|
+
if (!Node.isAwaitExpression(initializer))
|
|
54
|
+
return undefined;
|
|
55
|
+
const awaited = initializer.getExpression();
|
|
56
|
+
if (!Node.isCallExpression(awaited))
|
|
57
|
+
return undefined;
|
|
58
|
+
return resolveCallExpressionReturnType(awaited);
|
|
59
|
+
}
|
|
60
|
+
function resolveFromVariableDeclaration(identifier) {
|
|
61
|
+
if (!Node.isIdentifier(identifier))
|
|
62
|
+
return undefined;
|
|
63
|
+
const defs = identifier.getDefinitionNodes();
|
|
64
|
+
const [firstDef] = defs;
|
|
65
|
+
if (!firstDef || !Node.isVariableDeclaration(firstDef))
|
|
66
|
+
return undefined;
|
|
67
|
+
const typeNode = firstDef.getTypeNode();
|
|
68
|
+
if (typeNode)
|
|
69
|
+
return typeNode.getText();
|
|
70
|
+
const initializer = firstDef.getInitializer();
|
|
71
|
+
if (initializer)
|
|
72
|
+
return resolveAwaitedCallReturnType(initializer);
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
function resolveReceiverTypeName(receiver) {
|
|
76
|
+
if (Node.isCallExpression(receiver)) {
|
|
77
|
+
const returnType = resolveCallExpressionReturnType(receiver);
|
|
78
|
+
if (returnType)
|
|
79
|
+
return returnType;
|
|
80
|
+
}
|
|
81
|
+
const type = receiver.getType();
|
|
82
|
+
const rawName = extractTypeNameFromType(type);
|
|
83
|
+
if (isUnresolvableType(rawName)) {
|
|
84
|
+
const resolved = resolveFromVariableDeclaration(receiver);
|
|
85
|
+
if (resolved)
|
|
86
|
+
return resolved;
|
|
87
|
+
}
|
|
88
|
+
return rawName;
|
|
89
|
+
}
|
|
90
|
+
function buildError(sourceFile, callExpression, typeName, reason) {
|
|
91
|
+
return new ConnectionDetectionError({
|
|
92
|
+
file: sourceFile.getFilePath(),
|
|
93
|
+
line: callExpression.getStartLineNumber(),
|
|
94
|
+
typeName,
|
|
95
|
+
reason,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function handleUnresolvable(sourceFile, callExpression, rawTypeName, options) {
|
|
99
|
+
const reason = `Receiver type is '${rawTypeName}' — no concrete type to resolve`;
|
|
100
|
+
if (options.strict) {
|
|
101
|
+
throw buildError(sourceFile, callExpression, rawTypeName, reason);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
resolved: false,
|
|
105
|
+
reason,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function resolveCallExpressionReceiverType(callExpression, sourceFile, options) {
|
|
109
|
+
const receiver = getReceiverExpression(callExpression);
|
|
110
|
+
if (!receiver) {
|
|
111
|
+
const reason = 'Call expression has no property access receiver';
|
|
112
|
+
if (options.strict) {
|
|
113
|
+
throw buildError(sourceFile, callExpression, 'unknown', reason);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
resolved: false,
|
|
117
|
+
reason,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const rawTypeName = resolveReceiverTypeName(receiver);
|
|
121
|
+
if (isUnresolvableType(rawTypeName)) {
|
|
122
|
+
return handleUnresolvable(sourceFile, callExpression, rawTypeName, options);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
resolved: true,
|
|
126
|
+
typeName: stripGenerics(rawTypeName),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EnrichedComponent } from '../value-extraction/enrich-components';
|
|
2
|
+
export declare class ComponentIndex {
|
|
3
|
+
private readonly byName;
|
|
4
|
+
private readonly byLocation;
|
|
5
|
+
constructor(components: readonly EnrichedComponent[]);
|
|
6
|
+
isComponent(typeName: string): boolean;
|
|
7
|
+
getComponentByTypeName(typeName: string): EnrichedComponent | undefined;
|
|
8
|
+
getComponentByLocation(file: string, line: number): EnrichedComponent | undefined;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=component-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-index.d.ts","sourceRoot":"","sources":["../../../src/domain/connection-detection/component-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAO9E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwC;gBAEvD,UAAU,EAAE,SAAS,iBAAiB,EAAE;IAapD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIvE,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;CAGlF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { stripGenericArgs } from './call-graph/call-graph-types';
|
|
2
|
+
function locationKey(file, line) {
|
|
3
|
+
return `${file}:${line}`;
|
|
4
|
+
}
|
|
5
|
+
export class ComponentIndex {
|
|
6
|
+
byName;
|
|
7
|
+
byLocation;
|
|
8
|
+
constructor(components) {
|
|
9
|
+
const nameMap = new Map();
|
|
10
|
+
const locationMap = new Map();
|
|
11
|
+
for (const component of components) {
|
|
12
|
+
nameMap.set(component.name, component);
|
|
13
|
+
locationMap.set(locationKey(component.location.file, component.location.line), component);
|
|
14
|
+
}
|
|
15
|
+
this.byName = nameMap;
|
|
16
|
+
this.byLocation = locationMap;
|
|
17
|
+
}
|
|
18
|
+
isComponent(typeName) {
|
|
19
|
+
return this.byName.has(stripGenericArgs(typeName));
|
|
20
|
+
}
|
|
21
|
+
getComponentByTypeName(typeName) {
|
|
22
|
+
return this.byName.get(stripGenericArgs(typeName));
|
|
23
|
+
}
|
|
24
|
+
getComponentByLocation(file, line) {
|
|
25
|
+
return this.byLocation.get(locationKey(file, line));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class ConnectionDetectionError extends Error {
|
|
2
|
+
readonly file: string;
|
|
3
|
+
readonly line: number;
|
|
4
|
+
readonly typeName: string;
|
|
5
|
+
readonly reason: string;
|
|
6
|
+
constructor(params: {
|
|
7
|
+
file: string;
|
|
8
|
+
line: number;
|
|
9
|
+
typeName: string;
|
|
10
|
+
reason: string;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=connection-detection-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-detection-error.d.ts","sourceRoot":"","sources":["../../../src/domain/connection-detection/connection-detection-error.ts"],"names":[],"mappings":"AAAA,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAEX,MAAM,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAA;KACf;CAUF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class ConnectionDetectionError extends Error {
|
|
2
|
+
file;
|
|
3
|
+
line;
|
|
4
|
+
typeName;
|
|
5
|
+
reason;
|
|
6
|
+
constructor(params) {
|
|
7
|
+
super(`Connection detection failed for ${params.typeName} at ${params.file}:${params.line}: ${params.reason}`);
|
|
8
|
+
this.name = 'ConnectionDetectionError';
|
|
9
|
+
this.file = params.file;
|
|
10
|
+
this.line = params.line;
|
|
11
|
+
this.typeName = params.typeName;
|
|
12
|
+
this.reason = params.reason;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Project } from 'ts-morph';
|
|
2
|
+
import type { EnrichedComponent } from '../value-extraction/enrich-components';
|
|
3
|
+
import type { GlobMatcher } from '../component-extraction/extractor';
|
|
4
|
+
import type { ExtractedLink } from './extracted-link';
|
|
5
|
+
export interface ConnectionDetectionOptions {
|
|
6
|
+
allowIncomplete?: boolean;
|
|
7
|
+
moduleGlobs: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function detectConnections(project: Project, components: readonly EnrichedComponent[], options: ConnectionDetectionOptions, globMatcher: GlobMatcher): ExtractedLink[];
|
|
10
|
+
//# sourceMappingURL=detect-connections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-connections.d.ts","sourceRoot":"","sources":["../../../src/domain/connection-detection/detect-connections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAIrD,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAaD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,SAAS,iBAAiB,EAAE,EACxC,OAAO,EAAE,0BAA0B,EACnC,WAAW,EAAE,WAAW,GACvB,aAAa,EAAE,CAOjB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ComponentIndex } from './component-index';
|
|
2
|
+
import { buildCallGraph } from './call-graph/build-call-graph';
|
|
3
|
+
function computeFilteredFilePaths(project, moduleGlobs, globMatcher) {
|
|
4
|
+
return project
|
|
5
|
+
.getSourceFiles()
|
|
6
|
+
.map((sf) => sf.getFilePath())
|
|
7
|
+
.filter((filePath) => moduleGlobs.some((glob) => globMatcher(filePath, glob)));
|
|
8
|
+
}
|
|
9
|
+
export function detectConnections(project, components, options, globMatcher) {
|
|
10
|
+
const componentIndex = new ComponentIndex(components);
|
|
11
|
+
const sourceFilePaths = computeFilteredFilePaths(project, options.moduleGlobs, globMatcher);
|
|
12
|
+
return buildCallGraph(project, components, componentIndex, {
|
|
13
|
+
strict: !options.allowIncomplete,
|
|
14
|
+
sourceFilePaths,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extracted-link.d.ts","sourceRoot":"","sources":["../../../src/domain/connection-detection/extracted-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qCAAqC,CAAA;AAE/D,MAAM,WAAW,aAAc,SAAQ,IAAI;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
interface InterfaceResolutionResult {
|
|
3
|
+
resolved: true;
|
|
4
|
+
typeName: string;
|
|
5
|
+
}
|
|
6
|
+
interface InterfaceUnresolved {
|
|
7
|
+
resolved: false;
|
|
8
|
+
reason: string;
|
|
9
|
+
typeDefinedInSource: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type InterfaceResolution = InterfaceResolutionResult | InterfaceUnresolved;
|
|
12
|
+
export declare function resolveInterface(interfaceName: string, project: Project, sourceFilePaths: string[], options: {
|
|
13
|
+
strict: boolean;
|
|
14
|
+
}): InterfaceResolution;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=resolve-interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-interface.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/interface-resolution/resolve-interface.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACR,MAAM,UAAU,CAAA;AAGjB,UAAU,yBAAyB;IACjC,QAAQ,EAAE,IAAI,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,MAAM,mBAAmB,GAAG,yBAAyB,GAAG,mBAAmB,CAAA;AA+DjF,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC3B,mBAAmB,CAuCrB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Project, ClassDeclaration, SourceFile } from 'ts-morph';
|
|
2
|
+
import { ConnectionDetectionError } from '../connection-detection-error';
|
|
3
|
+
function isNodeModulesPath(filePath) {
|
|
4
|
+
return filePath.includes('node_modules');
|
|
5
|
+
}
|
|
6
|
+
function isMatchingClass(classDecl, interfaceName) {
|
|
7
|
+
return (implementsInterface(classDecl, interfaceName) || extendsAbstractClass(classDecl, interfaceName));
|
|
8
|
+
}
|
|
9
|
+
function getClassesFromFiles(sourceFiles) {
|
|
10
|
+
return sourceFiles.flatMap((sf) => sf.getClasses());
|
|
11
|
+
}
|
|
12
|
+
function findImplementations(interfaceName, sourceFiles) {
|
|
13
|
+
return getClassesFromFiles(sourceFiles)
|
|
14
|
+
.filter((classDecl) => isMatchingClass(classDecl, interfaceName))
|
|
15
|
+
.map((classDecl) => classDecl.getName())
|
|
16
|
+
.filter((name) => name !== undefined);
|
|
17
|
+
}
|
|
18
|
+
function isDefinedInSourceFiles(typeName, sourceFiles) {
|
|
19
|
+
return sourceFiles.some((sf) => sf.getInterfaces().some((i) => i.getName() === typeName) ||
|
|
20
|
+
sf.getClasses().some((c) => c.getName() === typeName && c.isAbstract()));
|
|
21
|
+
}
|
|
22
|
+
function getExpressionName(expression) {
|
|
23
|
+
return expression.getType().getSymbol()?.getName() ?? expression.getText();
|
|
24
|
+
}
|
|
25
|
+
function implementsInterface(classDecl, interfaceName) {
|
|
26
|
+
return classDecl
|
|
27
|
+
.getImplements()
|
|
28
|
+
.some((impl) => getExpressionName(impl.getExpression()) === interfaceName);
|
|
29
|
+
}
|
|
30
|
+
function extendsAbstractClass(classDecl, abstractName) {
|
|
31
|
+
const extendsClause = classDecl.getExtends();
|
|
32
|
+
if (extendsClause === undefined) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return getExpressionName(extendsClause.getExpression()) === abstractName;
|
|
36
|
+
}
|
|
37
|
+
function createError(interfaceName, reason) {
|
|
38
|
+
return new ConnectionDetectionError({
|
|
39
|
+
file: '',
|
|
40
|
+
line: 0,
|
|
41
|
+
typeName: interfaceName,
|
|
42
|
+
reason,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function resolveInterface(interfaceName, project, sourceFilePaths, options) {
|
|
46
|
+
const filteredPaths = sourceFilePaths.filter((p) => !isNodeModulesPath(p));
|
|
47
|
+
const sourceFiles = filteredPaths
|
|
48
|
+
.map((p) => project.getSourceFile(p))
|
|
49
|
+
.filter((sf) => sf !== undefined);
|
|
50
|
+
const implementations = findImplementations(interfaceName, sourceFiles);
|
|
51
|
+
const [singleImpl] = implementations;
|
|
52
|
+
if (implementations.length === 1 && singleImpl) {
|
|
53
|
+
return {
|
|
54
|
+
resolved: true,
|
|
55
|
+
typeName: singleImpl,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (implementations.length === 0) {
|
|
59
|
+
const definedInSource = isDefinedInSourceFiles(interfaceName, sourceFiles);
|
|
60
|
+
const reason = `No implementation found for ${interfaceName}`;
|
|
61
|
+
if (definedInSource && options.strict) {
|
|
62
|
+
throw createError(interfaceName, reason);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
resolved: false,
|
|
66
|
+
reason,
|
|
67
|
+
typeDefinedInSource: definedInSource,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const reason = `Multiple implementations found for ${interfaceName} (${implementations.length}): ${implementations.join(', ')}`;
|
|
71
|
+
if (options.strict) {
|
|
72
|
+
throw createError(interfaceName, reason);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
resolved: false,
|
|
76
|
+
reason,
|
|
77
|
+
typeDefinedInSource: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrich-components.d.ts","sourceRoot":"","sources":["../../../src/domain/value-extraction/enrich-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"enrich-components.d.ts","sourceRoot":"","sources":["../../../src/domain/value-extraction/enrich-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAC2B,OAAO,EAC7C,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EACV,wBAAwB,EAUzB,MAAM,6CAA6C,CAAA;AACpD,OAAO,KAAK,EACV,cAAc,EAAE,WAAW,EAC5B,MAAM,mCAAmC,CAAA;AAY1C,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAA;AAEzD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACvC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,cAAc,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,iBAAiB,EAAE,CAAA;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAA;CAC9B;AA6RD,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,wBAAwB,EAChC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAclB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { posix } from 'node:path';
|
|
2
|
-
import { evaluateLiteralRule, evaluateFromClassNameRule, evaluateFromFilePathRule, evaluateFromPropertyRule, } from './evaluate-extraction-rule';
|
|
2
|
+
import { evaluateLiteralRule, evaluateFromClassNameRule, evaluateFromFilePathRule, evaluateFromPropertyRule, evaluateFromMethodNameRule, } from './evaluate-extraction-rule';
|
|
3
|
+
import { evaluateFromGenericArgRule } from './evaluate-extraction-rule-generic';
|
|
3
4
|
import { ExtractionError } from '../../platform/domain/ast-literals/literal-detection';
|
|
4
5
|
function findMatchingModule(filePath, modules, globMatcher, configDir) {
|
|
5
6
|
const normalized = filePath.replaceAll(/\\+/g, '/');
|
|
@@ -54,6 +55,12 @@ function isFromFilePathRule(rule) {
|
|
|
54
55
|
function isFromPropertyRule(rule) {
|
|
55
56
|
return 'fromProperty' in rule;
|
|
56
57
|
}
|
|
58
|
+
function isFromMethodNameRule(rule) {
|
|
59
|
+
return 'fromMethodName' in rule;
|
|
60
|
+
}
|
|
61
|
+
function isFromGenericArgRule(rule) {
|
|
62
|
+
return 'fromGenericArg' in rule;
|
|
63
|
+
}
|
|
57
64
|
function findClassAtLine(project, draft) {
|
|
58
65
|
const sourceFile = project.getSourceFile(draft.location.file);
|
|
59
66
|
if (sourceFile === undefined) {
|
|
@@ -67,6 +74,36 @@ function findClassAtLine(project, draft) {
|
|
|
67
74
|
}
|
|
68
75
|
return classDecl;
|
|
69
76
|
}
|
|
77
|
+
function findMethodAtLine(project, draft) {
|
|
78
|
+
const sourceFile = project.getSourceFile(draft.location.file);
|
|
79
|
+
if (sourceFile === undefined) {
|
|
80
|
+
throw new ExtractionError(`Source file '${draft.location.file}' not found in project`, draft.location.file, draft.location.line);
|
|
81
|
+
}
|
|
82
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
83
|
+
const method = classDecl
|
|
84
|
+
.getMethods()
|
|
85
|
+
.find((m) => m.getStartLineNumber() === draft.location.line);
|
|
86
|
+
if (method !== undefined) {
|
|
87
|
+
return method;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
throw new ExtractionError(`No method declaration found at line ${draft.location.line}`, draft.location.file, draft.location.line);
|
|
91
|
+
}
|
|
92
|
+
function findContainingClass(project, draft) {
|
|
93
|
+
const sourceFile = project.getSourceFile(draft.location.file);
|
|
94
|
+
if (sourceFile === undefined) {
|
|
95
|
+
throw new ExtractionError(`Source file '${draft.location.file}' not found in project`, draft.location.file, draft.location.line);
|
|
96
|
+
}
|
|
97
|
+
const methodLine = draft.location.line;
|
|
98
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
99
|
+
const classStart = classDecl.getStartLineNumber();
|
|
100
|
+
const classEnd = classDecl.getEndLineNumber();
|
|
101
|
+
if (methodLine >= classStart && methodLine <= classEnd) {
|
|
102
|
+
return classDecl;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
throw new ExtractionError(`No containing class found for method at line ${methodLine}`, draft.location.file, draft.location.line);
|
|
106
|
+
}
|
|
70
107
|
function evaluateClassRule(rule, classDecl) {
|
|
71
108
|
if (isFromClassNameRule(rule)) {
|
|
72
109
|
return evaluateFromClassNameRule(rule, classDecl);
|
|
@@ -84,6 +121,14 @@ function evaluateRule(rule, draft, project) {
|
|
|
84
121
|
if (isFromFilePathRule(rule)) {
|
|
85
122
|
return evaluateFromFilePathRule(rule, draft.location.file);
|
|
86
123
|
}
|
|
124
|
+
if (isFromMethodNameRule(rule)) {
|
|
125
|
+
const methodDecl = findMethodAtLine(project, draft);
|
|
126
|
+
return evaluateFromMethodNameRule(rule, methodDecl);
|
|
127
|
+
}
|
|
128
|
+
if (isFromGenericArgRule(rule)) {
|
|
129
|
+
const classDecl = findContainingClass(project, draft);
|
|
130
|
+
return evaluateFromGenericArgRule(rule, classDecl);
|
|
131
|
+
}
|
|
87
132
|
const classDecl = findClassAtLine(project, draft);
|
|
88
133
|
return evaluateClassRule(rule, classDecl);
|
|
89
134
|
}
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -4,5 +4,9 @@ export { resolveConfig, type ConfigLoader } from '../domain/config-resolution/re
|
|
|
4
4
|
export { ConfigLoaderRequiredError, MissingComponentRuleError, } from '../domain/config-resolution/config-resolution-errors';
|
|
5
5
|
export { evaluateLiteralRule, evaluateFromClassNameRule, evaluateFromMethodNameRule, evaluateFromFilePathRule, evaluateFromPropertyRule, evaluateFromDecoratorArgRule, evaluateFromDecoratorNameRule, evaluateFromGenericArgRule, evaluateFromMethodSignatureRule, evaluateFromConstructorParamsRule, evaluateFromParameterTypeRule, applyTransforms, ExtractionError, type ExtractionContext, type ExtractionResult, type ParameterInfo, type MethodSignature, } from '../domain/value-extraction';
|
|
6
6
|
export { matchesGlob } from '../platform/infra/glob-matching/minimatch-glob';
|
|
7
|
+
export { detectConnections, type ConnectionDetectionOptions, } from '../domain/connection-detection/detect-connections';
|
|
8
|
+
export type { ExtractedLink } from '../domain/connection-detection/extracted-link';
|
|
9
|
+
export { ComponentIndex } from '../domain/connection-detection/component-index';
|
|
10
|
+
export { ConnectionDetectionError } from '../domain/connection-detection/connection-detection-error';
|
|
7
11
|
export { enrichComponents, type EnrichedComponent, type EnrichmentFailure, type EnrichmentResult, } from '../domain/value-extraction/enrich-components';
|
|
8
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,0CAA0C,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAA;AACrF,OAAO,EACL,aAAa,EAAE,KAAK,YAAY,EACjC,MAAM,4CAA4C,CAAA;AACnD,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sDAAsD,CAAA;AAC7D,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,+BAA+B,EAC/B,iCAAiC,EACjC,6BAA6B,EAC7B,eAAe,EACf,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,gDAAgD,CAAA;AAC5E,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,8CAA8C,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,0CAA0C,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAA;AACrF,OAAO,EACL,aAAa,EAAE,KAAK,YAAY,EACjC,MAAM,4CAA4C,CAAA;AACnD,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sDAAsD,CAAA;AAC7D,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,+BAA+B,EAC/B,iCAAiC,EACjC,6BAA6B,EAC7B,eAAe,EACf,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,gDAAgD,CAAA;AAC5E,OAAO,EACL,iBAAiB,EACjB,KAAK,0BAA0B,GAChC,MAAM,mDAAmD,CAAA;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,gDAAgD,CAAA;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAA;AACpG,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,8CAA8C,CAAA"}
|
package/dist/shell/index.js
CHANGED
|
@@ -4,4 +4,7 @@ export { resolveConfig } from '../domain/config-resolution/resolve-config';
|
|
|
4
4
|
export { ConfigLoaderRequiredError, MissingComponentRuleError, } from '../domain/config-resolution/config-resolution-errors';
|
|
5
5
|
export { evaluateLiteralRule, evaluateFromClassNameRule, evaluateFromMethodNameRule, evaluateFromFilePathRule, evaluateFromPropertyRule, evaluateFromDecoratorArgRule, evaluateFromDecoratorNameRule, evaluateFromGenericArgRule, evaluateFromMethodSignatureRule, evaluateFromConstructorParamsRule, evaluateFromParameterTypeRule, applyTransforms, ExtractionError, } from '../domain/value-extraction';
|
|
6
6
|
export { matchesGlob } from '../platform/infra/glob-matching/minimatch-glob';
|
|
7
|
+
export { detectConnections, } from '../domain/connection-detection/detect-connections';
|
|
8
|
+
export { ComponentIndex } from '../domain/connection-detection/component-index';
|
|
9
|
+
export { ConnectionDetectionError } from '../domain/connection-detection/connection-detection-error';
|
|
7
10
|
export { enrichComponents, } from '../domain/value-extraction/enrich-components';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@living-architecture/riviere-extract-ts",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"minimatch": "^10.0.1",
|
|
26
26
|
"ts-morph": "^24.0.0",
|
|
27
|
-
"@living-architecture/riviere-extract-config": "0.4.
|
|
27
|
+
"@living-architecture/riviere-extract-config": "0.4.2",
|
|
28
|
+
"@living-architecture/riviere-schema": "0.5.2"
|
|
28
29
|
}
|
|
29
30
|
}
|