@living-architecture/riviere-extract-ts 0.2.6 → 0.2.8
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/component-extraction/extractor.d.ts +1 -1
- package/dist/domain/component-extraction/extractor.d.ts.map +1 -1
- package/dist/domain/component-extraction/extractor.js +8 -15
- package/dist/domain/connection-detection/async-detection/detect-publish-connections.js +9 -0
- package/dist/domain/connection-detection/call-graph/build-call-graph.d.ts.map +1 -1
- package/dist/domain/connection-detection/call-graph/build-call-graph.js +32 -5
- package/dist/domain/connection-detection/call-graph/call-graph-shared.d.ts +1 -0
- package/dist/domain/connection-detection/call-graph/call-graph-shared.d.ts.map +1 -1
- package/dist/domain/connection-detection/call-graph/call-graph-shared.js +30 -10
- package/dist/domain/connection-detection/call-graph/trace-calls.d.ts +7 -1
- package/dist/domain/connection-detection/call-graph/trace-calls.d.ts.map +1 -1
- package/dist/domain/connection-detection/call-graph/trace-calls.js +98 -38
- package/dist/domain/connection-detection/detect-connections.js +1 -1
- package/dist/domain/value-extraction/enrich-components.d.ts.map +1 -1
- package/dist/domain/value-extraction/enrich-components.js +26 -34
- package/dist/platform/domain/ast-literals/literal-detection.d.ts +3 -0
- package/dist/platform/domain/ast-literals/literal-detection.d.ts.map +1 -1
- package/dist/platform/domain/ast-literals/literal-detection.js +32 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Project } from 'ts-morph';
|
|
2
2
|
import type { ResolvedExtractionConfig } from '@living-architecture/riviere-extract-config';
|
|
3
3
|
export type GlobMatcher = (path: string, pattern: string) => boolean;
|
|
4
4
|
export interface DraftComponent {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/domain/component-extraction/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/domain/component-extraction/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,OAAO,EAEb,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EACV,wBAAwB,EAIzB,MAAM,6CAA6C,CAAA;AAGpD,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;AAEpE,MAAM,WAAW,cAAc;IAC7B,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;CACf;AAYD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,MAAM,EAAE,wBAAwB,EAChC,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,cAAc,EAAE,CAIlB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Scope, } from 'ts-morph';
|
|
1
2
|
import { posix } from 'node:path';
|
|
2
3
|
import { evaluatePredicate } from '../predicate-evaluation/evaluate-predicate';
|
|
3
4
|
const COMPONENT_TYPES = [
|
|
@@ -6,22 +7,9 @@ const COMPONENT_TYPES = [
|
|
|
6
7
|
'domainOp',
|
|
7
8
|
'event',
|
|
8
9
|
'eventHandler',
|
|
10
|
+
'eventPublisher',
|
|
9
11
|
'ui',
|
|
10
12
|
];
|
|
11
|
-
const FIND_TARGETS = ['classes', 'methods', 'functions'];
|
|
12
|
-
function hasProperty(obj, key) {
|
|
13
|
-
return key in obj;
|
|
14
|
-
}
|
|
15
|
-
function isDetectionRule(rule) {
|
|
16
|
-
/* istanbul ignore if -- @preserve: unreachable with typed ResolvedExtractionConfig; defensive guard */
|
|
17
|
-
if (typeof rule !== 'object' || rule === null) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
if (!hasProperty(rule, 'find') || !hasProperty(rule, 'where')) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
return typeof rule.find === 'string' && FIND_TARGETS.includes(rule.find);
|
|
24
|
-
}
|
|
25
13
|
export function extractComponents(project, sourceFilePaths, config, globMatcher, configDir) {
|
|
26
14
|
return sourceFilePaths.flatMap((filePath) => extractFromFile(project, filePath, config, globMatcher, configDir));
|
|
27
15
|
}
|
|
@@ -63,7 +51,7 @@ function extractWithRule(sourceFile, filePath, domain, componentType, rule) {
|
|
|
63
51
|
}
|
|
64
52
|
function extractComponentType(sourceFile, filePath, module, componentType) {
|
|
65
53
|
const rule = module[componentType];
|
|
66
|
-
if (!
|
|
54
|
+
if (!('find' in rule)) {
|
|
67
55
|
return [];
|
|
68
56
|
}
|
|
69
57
|
return extractWithRule(sourceFile, filePath, module.name, componentType, rule);
|
|
@@ -78,6 +66,7 @@ function extractMethods(sourceFile, filePath, domain, componentType, rule) {
|
|
|
78
66
|
return sourceFile
|
|
79
67
|
.getClasses()
|
|
80
68
|
.flatMap((c) => c.getMethods())
|
|
69
|
+
.filter(isPublicMethod)
|
|
81
70
|
.filter((m) => evaluatePredicate(m, rule.where))
|
|
82
71
|
.flatMap((m) => createMethodComponent(m, filePath, domain, componentType));
|
|
83
72
|
}
|
|
@@ -87,6 +76,10 @@ function extractFunctions(sourceFile, filePath, domain, componentType, rule) {
|
|
|
87
76
|
.filter((f) => evaluatePredicate(f, rule.where))
|
|
88
77
|
.flatMap((f) => createFunctionComponent(f, filePath, domain, componentType));
|
|
89
78
|
}
|
|
79
|
+
function isPublicMethod(method) {
|
|
80
|
+
const scope = method.getScope();
|
|
81
|
+
return scope !== Scope.Private && scope !== Scope.Protected;
|
|
82
|
+
}
|
|
90
83
|
function createClassComponent(classDecl, filePath, domain, componentType) {
|
|
91
84
|
const name = classDecl.getName();
|
|
92
85
|
if (name === undefined) {
|
|
@@ -7,6 +7,15 @@ export function detectPublishConnections(project, components, options) {
|
|
|
7
7
|
return publishers.flatMap((publisher) => extractPublisherLinks(project, publisher, events, options));
|
|
8
8
|
}
|
|
9
9
|
function extractPublisherLinks(project, publisher, events, options) {
|
|
10
|
+
const publishedEventType = publisher.metadata['publishedEventType'];
|
|
11
|
+
if (typeof publishedEventType === 'string') {
|
|
12
|
+
const sourceLocation = {
|
|
13
|
+
repository: '',
|
|
14
|
+
filePath: publisher.location.file,
|
|
15
|
+
lineNumber: publisher.location.line,
|
|
16
|
+
};
|
|
17
|
+
return resolvePublishTarget(publisher, publishedEventType, events, options, sourceLocation);
|
|
18
|
+
}
|
|
10
19
|
const classDecl = findClassInProject(project, publisher);
|
|
11
20
|
if (classDecl === undefined) {
|
|
12
21
|
return [];
|
|
@@ -1 +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,
|
|
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,EAKL,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;AAwJ3B,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,SAAS,iBAAiB,EAAE,EACxC,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,gBAAgB,GACxB,aAAa,EAAE,CA0CjB"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Node, SyntaxKind, } from 'ts-morph';
|
|
2
2
|
import { componentIdentity } from './call-graph-types';
|
|
3
|
-
import { findClassInProject, traceCallsInBody } from './trace-calls';
|
|
3
|
+
import { findClassInProject, findFunctionInProject, findMethodLevelComponent, traceCallsInBody, } from './trace-calls';
|
|
4
4
|
import { deduplicateLinks } from './deduplicate-links';
|
|
5
5
|
import { resolveCallExpressionReceiverType } from './type-resolver';
|
|
6
|
-
import { getCalledMethodName, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
6
|
+
import { getCalledMethodName, resolveContainerMethod, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
7
7
|
function processCallExpression(callExpr, component, methodName, project, componentIndex, rawLinks, uncertainLinks, options) {
|
|
8
8
|
if (!Node.isPropertyAccessExpression(callExpr.getExpression())) {
|
|
9
9
|
return;
|
|
@@ -36,8 +36,26 @@ function processCallExpression(callExpr, component, methodName, project, compone
|
|
|
36
36
|
}
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
+
const containerTarget = resolveContainerMethod(project, resolvedTypeName ?? typeName, calledMethodName, componentIndex);
|
|
40
|
+
if (containerTarget !== undefined) {
|
|
41
|
+
if (componentIdentity(component) !== componentIdentity(containerTarget)) {
|
|
42
|
+
rawLinks.push({
|
|
43
|
+
source: component,
|
|
44
|
+
target: containerTarget,
|
|
45
|
+
callSite: currentCallSite,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
39
50
|
traceNonComponent(project, componentIndex, component, resolvedTypeName ?? typeName, calledMethodName, currentCallSite, rawLinks, uncertainLinks, uncertain, options);
|
|
40
51
|
}
|
|
52
|
+
function processFunction(funcDecl, component, project, componentIndex, rawLinks, uncertainLinks, options) {
|
|
53
|
+
const functionName = funcDecl.getNameOrThrow();
|
|
54
|
+
const callExpressions = funcDecl.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
55
|
+
for (const callExpr of callExpressions) {
|
|
56
|
+
processCallExpression(callExpr, component, functionName, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
41
59
|
function processMethod(method, component, project, componentIndex, rawLinks, uncertainLinks, options) {
|
|
42
60
|
const methodName = method.getName();
|
|
43
61
|
const callExpressions = method.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
@@ -50,11 +68,20 @@ export function buildCallGraph(project, components, componentIndex, options) {
|
|
|
50
68
|
const uncertainLinks = [];
|
|
51
69
|
for (const component of components) {
|
|
52
70
|
const classDecl = findClassInProject(project, component);
|
|
53
|
-
if (classDecl
|
|
71
|
+
if (classDecl !== undefined) {
|
|
72
|
+
for (const method of classDecl.getMethods()) {
|
|
73
|
+
processMethod(method, component, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const methodTarget = findMethodLevelComponent(project, component);
|
|
78
|
+
if (methodTarget !== undefined) {
|
|
79
|
+
processMethod(methodTarget.method, component, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
54
80
|
continue;
|
|
55
81
|
}
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
const funcDecl = findFunctionInProject(project, component);
|
|
83
|
+
if (funcDecl !== undefined) {
|
|
84
|
+
processFunction(funcDecl, component, project, componentIndex, rawLinks, uncertainLinks, options);
|
|
58
85
|
}
|
|
59
86
|
}
|
|
60
87
|
return deduplicateLinks(rawLinks, uncertainLinks);
|
|
@@ -13,5 +13,6 @@ export interface MethodLookup {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function getCalledMethodName(callExpr: CallExpression): string;
|
|
15
15
|
export declare function resolveTypeThroughInterface(typeName: string, project: Project, componentIndex: ComponentIndex, options: CallGraphOptions): InterfaceResolutionOutcome;
|
|
16
|
+
export declare function resolveContainerMethod(project: Project, typeName: string, calledMethodName: string, componentIndex: ComponentIndex): EnrichedComponent | undefined;
|
|
16
17
|
export declare function findMethodInProject(project: Project, typeName: string, methodName: string): MethodLookup;
|
|
17
18
|
//# sourceMappingURL=call-graph-shared.d.ts.map
|
|
@@ -1 +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,
|
|
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,EAEnB,KAAK,iBAAiB,EACtB,KAAK,OAAO,EAGb,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;AAqBD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,cAAc,GAC7B,iBAAiB,GAAG,SAAS,CAa/B;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,CAYd"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SyntaxKind } from 'ts-morph';
|
|
1
|
+
import { SyntaxKind, } from 'ts-morph';
|
|
2
2
|
import { resolveInterface } from '../interface-resolution/resolve-interface';
|
|
3
3
|
export function getCalledMethodName(callExpr) {
|
|
4
4
|
const expression = callExpr.getExpression();
|
|
@@ -27,20 +27,40 @@ export function resolveTypeThroughInterface(typeName, project, componentIndex, o
|
|
|
27
27
|
uncertain: interfaceResult.typeDefinedInSource ? interfaceResult.reason : undefined,
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
function findClassByNameInProject(project, typeName) {
|
|
31
31
|
for (const sourceFile of project.getSourceFiles()) {
|
|
32
32
|
for (const classDecl of sourceFile.getClasses()) {
|
|
33
|
-
if (classDecl.getName()
|
|
34
|
-
|
|
33
|
+
if (classDecl.getType().getSymbol()?.getName() === typeName) {
|
|
34
|
+
return {
|
|
35
|
+
classDecl,
|
|
36
|
+
sourceFile,
|
|
37
|
+
};
|
|
35
38
|
}
|
|
36
|
-
return {
|
|
37
|
-
method: classDecl.getMethod(methodName),
|
|
38
|
-
classFound: true,
|
|
39
|
-
};
|
|
40
39
|
}
|
|
41
40
|
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
export function resolveContainerMethod(project, typeName, calledMethodName, componentIndex) {
|
|
44
|
+
const lookup = findClassByNameInProject(project, typeName);
|
|
45
|
+
if (lookup === undefined) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const method = lookup.classDecl.getMethod(calledMethodName);
|
|
49
|
+
if (method === undefined) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return componentIndex.getComponentByLocation(lookup.sourceFile.getFilePath(), method.getStartLineNumber());
|
|
53
|
+
}
|
|
54
|
+
export function findMethodInProject(project, typeName, methodName) {
|
|
55
|
+
const lookup = findClassByNameInProject(project, typeName);
|
|
56
|
+
if (lookup === undefined) {
|
|
57
|
+
return {
|
|
58
|
+
method: undefined,
|
|
59
|
+
classFound: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
42
62
|
return {
|
|
43
|
-
method:
|
|
44
|
-
classFound:
|
|
63
|
+
method: lookup.classDecl.getMethod(methodName),
|
|
64
|
+
classFound: true,
|
|
45
65
|
};
|
|
46
66
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { type ClassDeclaration, type
|
|
1
|
+
import { type ClassDeclaration, type FunctionDeclaration, type MethodDeclaration, type Project } from 'ts-morph';
|
|
2
2
|
import type { ComponentIndex } from '../component-index';
|
|
3
3
|
import type { EnrichedComponent } from '../../value-extraction/enrich-components';
|
|
4
4
|
import type { CallGraphOptions, CallSite, RawLink, UncertainRawLink } from './call-graph-types';
|
|
5
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
6
|
export declare function findClassInProject(project: Project, component: EnrichedComponent): ClassDeclaration | undefined;
|
|
7
|
+
export interface MethodLevelTarget {
|
|
8
|
+
classDecl: ClassDeclaration;
|
|
9
|
+
method: MethodDeclaration;
|
|
10
|
+
}
|
|
11
|
+
export declare function findMethodLevelComponent(project: Project, component: EnrichedComponent): MethodLevelTarget | undefined;
|
|
12
|
+
export declare function findFunctionInProject(project: Project, component: EnrichedComponent): FunctionDeclaration | undefined;
|
|
7
13
|
//# sourceMappingURL=trace-calls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-calls.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/trace-calls.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"trace-calls.d.ts","sourceRoot":"","sources":["../../../../src/domain/connection-detection/call-graph/trace-calls.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,OAAO,EAEb,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;AA8H3B,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,CAWN;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,iBAAiB,GAC3B,gBAAgB,GAAG,SAAS,CAM9B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,gBAAgB,CAAA;IAC3B,MAAM,EAAE,iBAAiB,CAAA;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,iBAAiB,GAC3B,iBAAiB,GAAG,SAAS,CAiB/B;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,iBAAiB,GAC3B,mBAAmB,GAAG,SAAS,CAMjC"}
|
|
@@ -1,47 +1,82 @@
|
|
|
1
|
-
import { SyntaxKind } from 'ts-morph';
|
|
1
|
+
import { SyntaxKind, } from 'ts-morph';
|
|
2
2
|
import { componentIdentity } from './call-graph-types';
|
|
3
3
|
import { resolveCallExpressionReceiverType } from './type-resolver';
|
|
4
|
-
import { getCalledMethodName, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
else if (!classFound && uncertain !== undefined) {
|
|
37
|
-
uncertainResults.push({
|
|
38
|
-
source: sourceComponent,
|
|
39
|
-
reason: uncertain,
|
|
40
|
-
callSite: originCallSite,
|
|
4
|
+
import { getCalledMethodName, resolveContainerMethod, resolveTypeThroughInterface, findMethodInProject, } from './call-graph-shared';
|
|
5
|
+
function resolveComponentTarget(typeName, calledMethodName, ctx) {
|
|
6
|
+
const { component: directMatch, resolvedTypeName, uncertain, } = resolveTypeThroughInterface(typeName, ctx.project, ctx.componentIndex, ctx.options);
|
|
7
|
+
if (directMatch !== undefined) {
|
|
8
|
+
return {
|
|
9
|
+
target: directMatch,
|
|
10
|
+
resolvedTypeName: undefined,
|
|
11
|
+
uncertain: undefined,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const containerTarget = resolveContainerMethod(ctx.project, resolvedTypeName ?? typeName, calledMethodName, ctx.componentIndex);
|
|
15
|
+
return {
|
|
16
|
+
target: containerTarget,
|
|
17
|
+
resolvedTypeName,
|
|
18
|
+
uncertain,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function traceCallExpression(callExpr, ctx) {
|
|
22
|
+
const sourceFile = callExpr.getSourceFile();
|
|
23
|
+
const typeResult = resolveCallExpressionReceiverType(callExpr, sourceFile, { strict: ctx.options.strict, });
|
|
24
|
+
if (!typeResult.resolved) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const typeName = typeResult.typeName;
|
|
28
|
+
const calledMethodName = getCalledMethodName(callExpr);
|
|
29
|
+
const outcome = resolveComponentTarget(typeName, calledMethodName, ctx);
|
|
30
|
+
if (outcome.target !== undefined) {
|
|
31
|
+
if (componentIdentity(ctx.sourceComponent) !== componentIdentity(outcome.target)) {
|
|
32
|
+
ctx.results.push({
|
|
33
|
+
source: ctx.sourceComponent,
|
|
34
|
+
target: outcome.target,
|
|
35
|
+
callSite: ctx.originCallSite,
|
|
41
36
|
});
|
|
42
37
|
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
traceIntoNonComponent(typeName, calledMethodName, outcome, ctx);
|
|
41
|
+
}
|
|
42
|
+
function traceIntoNonComponent(typeName, calledMethodName, outcome, ctx) {
|
|
43
|
+
const traceTypeName = outcome.resolvedTypeName ?? typeName;
|
|
44
|
+
const visitKey = `${traceTypeName}.${calledMethodName}`;
|
|
45
|
+
if (ctx.visited.has(visitKey)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
ctx.visited.add(visitKey);
|
|
49
|
+
const { method: resolvedMethod, classFound } = findMethodInProject(ctx.project, traceTypeName, calledMethodName);
|
|
50
|
+
if (resolvedMethod !== undefined) {
|
|
51
|
+
traceBody(resolvedMethod, ctx);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!classFound && outcome.uncertain !== undefined) {
|
|
55
|
+
ctx.uncertainResults.push({
|
|
56
|
+
source: ctx.sourceComponent,
|
|
57
|
+
reason: outcome.uncertain,
|
|
58
|
+
callSite: ctx.originCallSite,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function traceBody(body, ctx) {
|
|
63
|
+
const callExpressions = body.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
64
|
+
for (const callExpr of callExpressions) {
|
|
65
|
+
traceCallExpression(callExpr, ctx);
|
|
43
66
|
}
|
|
44
67
|
}
|
|
68
|
+
export function traceCallsInBody(body, project, componentIndex, sourceComponent, originCallSite, visited, results, uncertainResults, options) {
|
|
69
|
+
traceBody(body, {
|
|
70
|
+
project,
|
|
71
|
+
componentIndex,
|
|
72
|
+
sourceComponent,
|
|
73
|
+
originCallSite,
|
|
74
|
+
visited,
|
|
75
|
+
results,
|
|
76
|
+
uncertainResults,
|
|
77
|
+
options,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
45
80
|
export function findClassInProject(project, component) {
|
|
46
81
|
const sourceFile = project.getSourceFile(component.location.file);
|
|
47
82
|
if (sourceFile === undefined) {
|
|
@@ -49,3 +84,28 @@ export function findClassInProject(project, component) {
|
|
|
49
84
|
}
|
|
50
85
|
return sourceFile.getClasses().find((c) => c.getStartLineNumber() === component.location.line);
|
|
51
86
|
}
|
|
87
|
+
export function findMethodLevelComponent(project, component) {
|
|
88
|
+
const sourceFile = project.getSourceFile(component.location.file);
|
|
89
|
+
if (sourceFile === undefined) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
93
|
+
const method = classDecl
|
|
94
|
+
.getMethods()
|
|
95
|
+
.find((m) => m.getStartLineNumber() === component.location.line);
|
|
96
|
+
if (method !== undefined) {
|
|
97
|
+
return {
|
|
98
|
+
classDecl,
|
|
99
|
+
method,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
export function findFunctionInProject(project, component) {
|
|
106
|
+
const sourceFile = project.getSourceFile(component.location.file);
|
|
107
|
+
if (sourceFile === undefined) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
return sourceFile.getFunctions().find((f) => f.getStartLineNumber() === component.location.line);
|
|
111
|
+
}
|
|
@@ -15,7 +15,7 @@ export function detectConnections(project, components, options, globMatcher) {
|
|
|
15
15
|
const componentIndex = new ComponentIndex(components);
|
|
16
16
|
const sourceFilePaths = computeFilteredFilePaths(project, options.moduleGlobs, globMatcher);
|
|
17
17
|
const setupMs = performance.now() - setupStart;
|
|
18
|
-
const strict =
|
|
18
|
+
const strict = options.allowIncomplete !== true;
|
|
19
19
|
const callGraphStart = performance.now();
|
|
20
20
|
const syncLinks = buildCallGraph(project, components, componentIndex, {
|
|
21
21
|
strict,
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,EAKzB,MAAM,6CAA6C,CAAA;AACpD,OAAO,KAAK,EACV,cAAc,EAAE,WAAW,EAC5B,MAAM,mCAAmC,CAAA;AAa1C,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;AAmRD,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"}
|
|
@@ -2,18 +2,12 @@ import { posix } from 'node:path';
|
|
|
2
2
|
import { evaluateLiteralRule, evaluateFromClassNameRule, evaluateFromFilePathRule, evaluateFromPropertyRule, evaluateFromMethodNameRule, } from './evaluate-extraction-rule';
|
|
3
3
|
import { evaluateFromGenericArgRule } from './evaluate-extraction-rule-generic';
|
|
4
4
|
import { ExtractionError } from '../../platform/domain/ast-literals/literal-detection';
|
|
5
|
+
import { applyTransforms } from '../../platform/domain/string-transforms/transforms';
|
|
5
6
|
function findMatchingModule(filePath, modules, globMatcher, configDir) {
|
|
6
7
|
const normalized = filePath.replaceAll(/\\+/g, '/');
|
|
7
8
|
const pathToMatch = posix.relative(configDir.replaceAll(/\\+/g, '/'), normalized);
|
|
8
9
|
return modules.find((m) => globMatcher(pathToMatch, m.path));
|
|
9
10
|
}
|
|
10
|
-
function isDetectionRule(rule) {
|
|
11
|
-
/* istanbul ignore if -- @preserve: unreachable with typed ResolvedExtractionConfig; defensive guard */
|
|
12
|
-
if (typeof rule !== 'object' || rule === null) {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
return 'find' in rule && 'where' in rule;
|
|
16
|
-
}
|
|
17
11
|
function getBuiltInRule(module, componentType) {
|
|
18
12
|
const ruleMap = {
|
|
19
13
|
api: module.api,
|
|
@@ -25,10 +19,10 @@ function getBuiltInRule(module, componentType) {
|
|
|
25
19
|
ui: module.ui,
|
|
26
20
|
};
|
|
27
21
|
const rule = ruleMap[componentType];
|
|
28
|
-
if (
|
|
29
|
-
return
|
|
22
|
+
if (rule === undefined || !('find' in rule)) {
|
|
23
|
+
return undefined;
|
|
30
24
|
}
|
|
31
|
-
return
|
|
25
|
+
return rule;
|
|
32
26
|
}
|
|
33
27
|
function findDetectionRule(module, componentType) {
|
|
34
28
|
const builtInTypes = [
|
|
@@ -37,6 +31,7 @@ function findDetectionRule(module, componentType) {
|
|
|
37
31
|
'domainOp',
|
|
38
32
|
'event',
|
|
39
33
|
'eventHandler',
|
|
34
|
+
'eventPublisher',
|
|
40
35
|
'ui',
|
|
41
36
|
];
|
|
42
37
|
if (builtInTypes.includes(componentType)) {
|
|
@@ -44,24 +39,6 @@ function findDetectionRule(module, componentType) {
|
|
|
44
39
|
}
|
|
45
40
|
return module.customTypes?.[componentType];
|
|
46
41
|
}
|
|
47
|
-
function isLiteralRule(rule) {
|
|
48
|
-
return 'literal' in rule;
|
|
49
|
-
}
|
|
50
|
-
function isFromClassNameRule(rule) {
|
|
51
|
-
return 'fromClassName' in rule;
|
|
52
|
-
}
|
|
53
|
-
function isFromFilePathRule(rule) {
|
|
54
|
-
return 'fromFilePath' in rule;
|
|
55
|
-
}
|
|
56
|
-
function isFromPropertyRule(rule) {
|
|
57
|
-
return 'fromProperty' in rule;
|
|
58
|
-
}
|
|
59
|
-
function isFromMethodNameRule(rule) {
|
|
60
|
-
return 'fromMethodName' in rule;
|
|
61
|
-
}
|
|
62
|
-
function isFromGenericArgRule(rule) {
|
|
63
|
-
return 'fromGenericArg' in rule;
|
|
64
|
-
}
|
|
65
42
|
function findClassAtLine(project, draft) {
|
|
66
43
|
const sourceFile = project.getSourceFile(draft.location.file);
|
|
67
44
|
if (sourceFile === undefined) {
|
|
@@ -106,30 +83,45 @@ function findContainingClass(project, draft) {
|
|
|
106
83
|
throw new ExtractionError(`No containing class found for method at line ${methodLine}`, draft.location.file, draft.location.line);
|
|
107
84
|
}
|
|
108
85
|
function evaluateClassRule(rule, classDecl) {
|
|
109
|
-
if (
|
|
86
|
+
if ('fromClassName' in rule) {
|
|
110
87
|
return evaluateFromClassNameRule(rule, classDecl);
|
|
111
88
|
}
|
|
112
89
|
/* istanbul ignore next -- @preserve: only fromProperty reaches here; defensive guard */
|
|
113
|
-
if (!
|
|
90
|
+
if (!('fromProperty' in rule)) {
|
|
114
91
|
throw new ExtractionError('Unsupported extraction rule type for class-based component', classDecl.getSourceFile().getFilePath(), classDecl.getStartLineNumber());
|
|
115
92
|
}
|
|
116
93
|
return evaluateFromPropertyRule(rule, classDecl);
|
|
117
94
|
}
|
|
118
95
|
function evaluateRule(rule, draft, project) {
|
|
119
|
-
if (
|
|
96
|
+
if ('literal' in rule) {
|
|
120
97
|
return evaluateLiteralRule(rule);
|
|
121
98
|
}
|
|
122
|
-
if (
|
|
99
|
+
if ('fromFilePath' in rule) {
|
|
123
100
|
return evaluateFromFilePathRule(rule, draft.location.file);
|
|
124
101
|
}
|
|
125
|
-
if (
|
|
102
|
+
if ('fromMethodName' in rule) {
|
|
126
103
|
const methodDecl = findMethodAtLine(project, draft);
|
|
127
104
|
return evaluateFromMethodNameRule(rule, methodDecl);
|
|
128
105
|
}
|
|
129
|
-
if (
|
|
106
|
+
if ('fromGenericArg' in rule) {
|
|
130
107
|
const classDecl = findContainingClass(project, draft);
|
|
131
108
|
return evaluateFromGenericArgRule(rule, classDecl);
|
|
132
109
|
}
|
|
110
|
+
if ('fromParameterType' in rule) {
|
|
111
|
+
const methodDecl = findMethodAtLine(project, draft);
|
|
112
|
+
const params = methodDecl.getParameters();
|
|
113
|
+
const position = rule.fromParameterType.position;
|
|
114
|
+
const param = params[position];
|
|
115
|
+
if (param === undefined) {
|
|
116
|
+
throw new ExtractionError(`Parameter position ${position} out of bounds. Method has ${params.length} parameter(s)`, draft.location.file, draft.location.line);
|
|
117
|
+
}
|
|
118
|
+
const typeName = param.getTypeNode()?.getText() ?? 'unknown';
|
|
119
|
+
const transform = rule.fromParameterType.transform;
|
|
120
|
+
if (transform === undefined) {
|
|
121
|
+
return { value: typeName };
|
|
122
|
+
}
|
|
123
|
+
return { value: applyTransforms(typeName, transform) };
|
|
124
|
+
}
|
|
133
125
|
const classDecl = findClassAtLine(project, draft);
|
|
134
126
|
return evaluateClassRule(rule, classDecl);
|
|
135
127
|
}
|
|
@@ -19,6 +19,9 @@ export type LiteralResult = {
|
|
|
19
19
|
} | {
|
|
20
20
|
kind: 'boolean';
|
|
21
21
|
value: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'string[]';
|
|
24
|
+
value: string[];
|
|
22
25
|
};
|
|
23
26
|
export declare function extractLiteralValue(expression: Expression | undefined, file: string, line: number): LiteralResult;
|
|
24
27
|
//# sourceMappingURL=literal-detection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"literal-detection.d.ts","sourceRoot":"","sources":["../../../../src/platform/domain/ast-literals/literal-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1C,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,QAAQ,EAAE;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;gBAEW,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQxD;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"literal-detection.d.ts","sourceRoot":"","sources":["../../../../src/platform/domain/ast-literals/literal-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1C,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,QAAQ,EAAE;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;gBAEW,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQxD;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAa1E;AAUD,MAAM,MAAM,aAAa,GACrB;IACA,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd,GACC;IACA,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd,GACC;IACA,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf,GACC;IACA,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AA0EH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,aAAa,CAWf"}
|
|
@@ -24,7 +24,15 @@ export function isLiteralValue(expression) {
|
|
|
24
24
|
return (kind === SyntaxKind.StringLiteral ||
|
|
25
25
|
kind === SyntaxKind.NumericLiteral ||
|
|
26
26
|
kind === SyntaxKind.TrueKeyword ||
|
|
27
|
-
kind === SyntaxKind.FalseKeyword
|
|
27
|
+
kind === SyntaxKind.FalseKeyword ||
|
|
28
|
+
isStringArrayLiteral(expression));
|
|
29
|
+
}
|
|
30
|
+
function isStringArrayLiteral(expression) {
|
|
31
|
+
if (expression.getKind() !== SyntaxKind.ArrayLiteralExpression) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const elements = expression.asKindOrThrow(SyntaxKind.ArrayLiteralExpression).getElements();
|
|
35
|
+
return elements.every((e) => e.getKind() === SyntaxKind.StringLiteral);
|
|
28
36
|
}
|
|
29
37
|
function extractString(expression) {
|
|
30
38
|
return expression.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
@@ -32,6 +40,18 @@ function extractString(expression) {
|
|
|
32
40
|
function extractNumber(expression) {
|
|
33
41
|
return Number(expression.getText());
|
|
34
42
|
}
|
|
43
|
+
function extractStringArray(expression) {
|
|
44
|
+
const arrayLiteral = expression.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
45
|
+
const elements = arrayLiteral.getElements();
|
|
46
|
+
const values = [];
|
|
47
|
+
for (const element of elements) {
|
|
48
|
+
if (element.getKind() !== SyntaxKind.StringLiteral) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
values.push(element.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue());
|
|
52
|
+
}
|
|
53
|
+
return values;
|
|
54
|
+
}
|
|
35
55
|
function buildExtractionResult(expression) {
|
|
36
56
|
const syntaxKind = expression.getKind();
|
|
37
57
|
switch (syntaxKind) {
|
|
@@ -55,6 +75,16 @@ function buildExtractionResult(expression) {
|
|
|
55
75
|
kind: 'boolean',
|
|
56
76
|
value: false,
|
|
57
77
|
};
|
|
78
|
+
case SyntaxKind.ArrayLiteralExpression: {
|
|
79
|
+
const values = extractStringArray(expression);
|
|
80
|
+
if (values === undefined) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
kind: 'string[]',
|
|
85
|
+
value: values,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
58
88
|
default:
|
|
59
89
|
return undefined;
|
|
60
90
|
}
|
|
@@ -63,7 +93,7 @@ function throwMissingInitializer(file, line) {
|
|
|
63
93
|
throw new ExtractionError('No initializer found', file, line);
|
|
64
94
|
}
|
|
65
95
|
function throwNonLiteralValue(expression, file, line) {
|
|
66
|
-
throw new ExtractionError(`Non-literal value detected (${expression.getKindName()}): ${expression.getText()}. Only inline literals (strings, numbers, booleans) are supported`, file, line);
|
|
96
|
+
throw new ExtractionError(`Non-literal value detected (${expression.getKindName()}): ${expression.getText()}. Only inline literals (strings, numbers, booleans, string arrays) are supported`, file, line);
|
|
67
97
|
}
|
|
68
98
|
export function extractLiteralValue(expression, file, line) {
|
|
69
99
|
if (expression === undefined) {
|