@saptools/service-flow 0.1.66 → 0.1.68
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/CHANGELOG.md +14 -0
- package/README.md +20 -8
- package/TECHNICAL-NOTE.md +27 -0
- package/dist/{chunk-TOVX4WYH.js → chunk-AEM4JY22.js} +12676 -6714
- package/dist/chunk-AEM4JY22.js.map +1 -0
- package/dist/cli.js +2325 -413
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +61 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/db/000-call-fact-repository.ts +475 -342
- package/src/db/001-fact-lifecycle.ts +60 -30
- package/src/db/002-fact-json-inventory.ts +169 -0
- package/src/db/003-current-fact-semantics.ts +698 -0
- package/src/db/004-package-target-invalidation.ts +173 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +443 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +207 -0
- package/src/db/009-binding-fact-semantics.ts +347 -0
- package/src/db/010-package-symbol-surface-semantics.ts +320 -0
- package/src/db/011-symbol-call-semantics.ts +144 -0
- package/src/db/012-binding-reference-proof.ts +264 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +113 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/repository-indexer.ts +72 -11
- package/src/indexer/workspace-indexer.ts +123 -32
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +2 -0
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/cross-repo-linker.ts +2 -1
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/002-symbol-import-bindings.ts +516 -0
- package/src/parsers/003-package-public-surface.ts +661 -0
- package/src/parsers/004-fact-identity.ts +108 -0
- package/src/parsers/005-event-subscription-facts.ts +281 -0
- package/src/parsers/006-binding-identity.ts +343 -0
- package/src/parsers/007-source-fact-reconciliation.ts +105 -0
- package/src/parsers/008-package-surface-publication.ts +82 -0
- package/src/parsers/009-symbol-call-facts.ts +528 -0
- package/src/parsers/010-package-public-surface-analysis.ts +352 -0
- package/src/parsers/011-binding-lexical-scope.ts +583 -0
- package/src/parsers/012-package-fact-contract.ts +306 -0
- package/src/parsers/013-executable-body-eligibility.ts +35 -0
- package/src/parsers/014-service-binding-helper-flow.ts +240 -0
- package/src/parsers/015-service-binding-collector.ts +693 -0
- package/src/parsers/016-local-symbol-reference.ts +261 -0
- package/src/parsers/017-symbol-derived-contexts.ts +268 -0
- package/src/parsers/018-package-commonjs-syntax.ts +142 -0
- package/src/parsers/019-binding-assignment-targets.ts +76 -0
- package/src/parsers/020-stable-local-value.ts +217 -0
- package/src/parsers/021-binding-visibility.ts +152 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +19 -6
- package/src/parsers/package-json-parser.ts +45 -3
- package/src/parsers/service-binding-parser-helpers.ts +86 -15
- package/src/parsers/service-binding-parser.ts +147 -597
- package/src/parsers/symbol-parser.ts +482 -353
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/016-compact-projector.ts +16 -17
- package/src/trace/020-compact-field-projection.ts +48 -37
- package/src/trace/021-compact-decision-normalization.ts +105 -0
- package/src/trace/022-trace-fact-preflight.ts +21 -0
- package/src/trace/023-nested-event-scopes.ts +23 -0
- package/src/trace/024-compact-observation-decision.ts +76 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +335 -0
- package/src/trace/027-trace-scope-execution.ts +566 -0
- package/src/trace/028-trace-operation-execution.ts +336 -0
- package/src/trace/029-trace-start-implementation.ts +172 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +56 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-TOVX4WYH.js.map +0 -1
|
@@ -3,642 +3,192 @@ import ts from 'typescript';
|
|
|
3
3
|
import type { ServiceBindingFact } from '../types.js';
|
|
4
4
|
import { normalizePath } from '../utils/path-utils.js';
|
|
5
5
|
import {
|
|
6
|
-
connectFactFromCall,
|
|
7
|
-
findConnectInExpression,
|
|
8
6
|
importsFor,
|
|
9
7
|
lineOf,
|
|
10
8
|
readSource,
|
|
11
|
-
transactionReceiverName,
|
|
12
|
-
unwrapCall,
|
|
13
|
-
unwrapIdentityExpression,
|
|
14
9
|
type ClassHelperReturn,
|
|
15
10
|
type HelperBinding,
|
|
16
|
-
type ImportBinding,
|
|
17
11
|
} from './service-binding-parser-helpers.js';
|
|
12
|
+
import {
|
|
13
|
+
collectReturnedObjectBindings,
|
|
14
|
+
directConnectFact,
|
|
15
|
+
directConnectFactFromFunctionLike,
|
|
16
|
+
functionLikeInitializer,
|
|
17
|
+
type LocalBindingFact,
|
|
18
|
+
} from './014-service-binding-helper-flow.js';
|
|
19
|
+
import { collectServiceBindings } from './015-service-binding-collector.js';
|
|
18
20
|
import type { RepositorySourceContext } from './ts-project.js';
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const bindings = new Map<string, Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>>();
|
|
24
|
-
function visit(node: ts.Node): void {
|
|
25
|
-
if (node !== fn && (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)))
|
|
26
|
-
return;
|
|
27
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
28
|
-
const fact = findConnectInExpression(node.initializer);
|
|
29
|
-
if (fact) bindings.set(node.name.text, fact);
|
|
30
|
-
const sourceName = transactionReceiverName(node.initializer);
|
|
31
|
-
if (sourceName) {
|
|
32
|
-
const source = bindings.get(sourceName);
|
|
33
|
-
if (source) bindings.set(node.name.text, { ...source, helperChain: [...(source.helperChain ?? []), { aliasOf: sourceName, callerVariable: node.name.text, aliasKind: 'transaction', transactionAliasSource: sourceName }] });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
ts.forEachChild(node, visit);
|
|
37
|
-
}
|
|
38
|
-
visit(fn);
|
|
39
|
-
return bindings;
|
|
22
|
+
interface LocalHelperFact extends LocalBindingFact {
|
|
23
|
+
sourceLine: number;
|
|
24
|
+
returnedProperty?: string;
|
|
40
25
|
}
|
|
41
26
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (ts.isShorthandPropertyAssignment(prop)) returns.set(prop.name.text, prop.name.text);
|
|
53
|
-
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.initializer)) {
|
|
54
|
-
const propertyName = ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name) ? prop.name.text : undefined;
|
|
55
|
-
if (propertyName) returns.set(propertyName, prop.initializer.text);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
ts.forEachChild(node, visit);
|
|
60
|
-
}
|
|
61
|
-
visit(fn);
|
|
62
|
-
const out = new Map<string, Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>>();
|
|
63
|
-
for (const [propertyName, variableName] of returns) {
|
|
64
|
-
const fact = bindings.get(variableName);
|
|
65
|
-
if (fact) out.set(propertyName, fact);
|
|
66
|
-
}
|
|
67
|
-
return out;
|
|
27
|
+
function directlyExportedNames(statement: ts.Statement): string[] {
|
|
28
|
+
const direct = ts.canHaveModifiers(statement)
|
|
29
|
+
&& (ts.getModifiers(statement)?.some((modifier) =>
|
|
30
|
+
modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false);
|
|
31
|
+
if (!direct) return [];
|
|
32
|
+
if (ts.isFunctionDeclaration(statement) && statement.name)
|
|
33
|
+
return [statement.name.text];
|
|
34
|
+
if (!ts.isVariableStatement(statement)) return [];
|
|
35
|
+
return statement.declarationList.declarations.flatMap((declaration) =>
|
|
36
|
+
ts.isIdentifier(declaration.name) ? [declaration.name.text] : []);
|
|
68
37
|
}
|
|
69
38
|
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (ts.
|
|
75
|
-
|
|
39
|
+
function namedExports(statement: ts.Statement): Array<{
|
|
40
|
+
exportedName: string;
|
|
41
|
+
localName: string;
|
|
42
|
+
}> {
|
|
43
|
+
if (!ts.isExportDeclaration(statement)
|
|
44
|
+
|| !statement.exportClause
|
|
45
|
+
|| !ts.isNamedExports(statement.exportClause)) return [];
|
|
46
|
+
return statement.exportClause.elements.map((element) => ({
|
|
47
|
+
exportedName: element.name.text,
|
|
48
|
+
localName: element.propertyName?.text ?? element.name.text,
|
|
49
|
+
}));
|
|
76
50
|
}
|
|
77
51
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return;
|
|
86
|
-
if (!returned && ts.isReturnStatement(node) && node.expression)
|
|
87
|
-
returned = node.expression;
|
|
88
|
-
if (!returned) ts.forEachChild(node, visit);
|
|
52
|
+
function exportedLocalNames(source: ts.SourceFile): Map<string, string> {
|
|
53
|
+
const exports = new Map<string, string>();
|
|
54
|
+
for (const statement of source.statements) {
|
|
55
|
+
for (const name of directlyExportedNames(statement))
|
|
56
|
+
exports.set(name, name);
|
|
57
|
+
for (const entry of namedExports(statement))
|
|
58
|
+
exports.set(entry.exportedName, entry.localName);
|
|
89
59
|
}
|
|
90
|
-
|
|
91
|
-
if (!returned) return undefined;
|
|
92
|
-
if (ts.isIdentifier(returned)) return localBindings.get(returned.text);
|
|
93
|
-
return findConnectInExpression(returned);
|
|
60
|
+
return exports;
|
|
94
61
|
}
|
|
95
62
|
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
63
|
+
function recordFunctionFacts(
|
|
64
|
+
facts: Map<string, LocalHelperFact>,
|
|
65
|
+
localName: string,
|
|
66
|
+
node: ts.Node,
|
|
67
|
+
helper: ts.FunctionLikeDeclaration,
|
|
68
|
+
source: ts.SourceFile,
|
|
69
|
+
): void {
|
|
70
|
+
const sourceLine = lineOf(source, node);
|
|
71
|
+
const direct = directConnectFactFromFunctionLike(helper);
|
|
72
|
+
if (direct) facts.set(localName, { ...direct, sourceLine });
|
|
73
|
+
for (const [returnedProperty, fact] of collectReturnedObjectBindings(helper))
|
|
74
|
+
facts.set(`${localName}#${returnedProperty}`, {
|
|
75
|
+
...fact,
|
|
76
|
+
returnedProperty,
|
|
77
|
+
sourceLine,
|
|
78
|
+
});
|
|
102
79
|
}
|
|
103
80
|
|
|
104
|
-
function
|
|
105
|
-
const
|
|
106
|
-
for (const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
exports.set(el.name.text, el.propertyName?.text ?? el.name.text);
|
|
123
|
-
}
|
|
124
|
-
return exports;
|
|
125
|
-
}
|
|
126
|
-
async function helperBindings(
|
|
127
|
-
repoPath: string,
|
|
128
|
-
filePath: string,
|
|
129
|
-
context?: RepositorySourceContext,
|
|
130
|
-
): Promise<HelperBinding[]> {
|
|
131
|
-
const sf = await readSource(path.join(repoPath, filePath), context, filePath);
|
|
132
|
-
if (!sf) return [];
|
|
133
|
-
const sourceFileAst = sf;
|
|
134
|
-
const out: HelperBinding[] = [];
|
|
135
|
-
const exportedLocals = exportedLocalNames(sf);
|
|
136
|
-
const factsByLocal = new Map<
|
|
137
|
-
string,
|
|
138
|
-
Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'> & {
|
|
139
|
-
sourceLine: number;
|
|
140
|
-
}
|
|
141
|
-
>();
|
|
142
|
-
for (const stmt of sf.statements) {
|
|
143
|
-
if (ts.isFunctionDeclaration(stmt) && stmt.name) {
|
|
144
|
-
const fact = directConnectFactFromFunctionLike(stmt);
|
|
145
|
-
if (fact) factsByLocal.set(stmt.name.text, { ...fact, sourceLine: lineOf(sf, stmt) });
|
|
146
|
-
for (const [returnedProperty, objectFact] of collectReturnedObjectBindings(stmt))
|
|
147
|
-
factsByLocal.set(`${stmt.name.text}#${returnedProperty}`, { ...objectFact, returnedProperty, sourceLine: lineOf(sf, stmt) });
|
|
148
|
-
}
|
|
149
|
-
if (ts.isVariableStatement(stmt))
|
|
150
|
-
for (const decl of stmt.declarationList.declarations) {
|
|
151
|
-
if (!ts.isIdentifier(decl.name) || !decl.initializer) continue;
|
|
152
|
-
const helper = functionLikeInitializer(decl.initializer);
|
|
153
|
-
if (helper) {
|
|
154
|
-
const directReturn = directConnectFactFromFunctionLike(helper);
|
|
155
|
-
if (directReturn)
|
|
156
|
-
factsByLocal.set(decl.name.text, {
|
|
157
|
-
...directReturn,
|
|
158
|
-
sourceLine: lineOf(sourceFileAst, decl),
|
|
159
|
-
});
|
|
160
|
-
for (const [returnedProperty, objectFact] of collectReturnedObjectBindings(helper))
|
|
161
|
-
factsByLocal.set(`${decl.name.text}#${returnedProperty}`, {
|
|
162
|
-
...objectFact,
|
|
163
|
-
returnedProperty,
|
|
164
|
-
sourceLine: lineOf(sourceFileAst, decl),
|
|
165
|
-
});
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
const fact = findConnectInExpression(decl.initializer);
|
|
81
|
+
function localHelperFacts(source: ts.SourceFile): Map<string, LocalHelperFact> {
|
|
82
|
+
const facts = new Map<string, LocalHelperFact>();
|
|
83
|
+
for (const statement of source.statements) {
|
|
84
|
+
if (ts.isFunctionDeclaration(statement) && statement.name)
|
|
85
|
+
recordFunctionFacts(
|
|
86
|
+
facts, statement.name.text, statement, statement, source,
|
|
87
|
+
);
|
|
88
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
89
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
90
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
|
|
91
|
+
continue;
|
|
92
|
+
const helper = functionLikeInitializer(declaration.initializer);
|
|
93
|
+
if (helper)
|
|
94
|
+
recordFunctionFacts(
|
|
95
|
+
facts, declaration.name.text, declaration, helper, source,
|
|
96
|
+
);
|
|
97
|
+
else {
|
|
98
|
+
const fact = directConnectFact(declaration.initializer);
|
|
169
99
|
if (fact)
|
|
170
|
-
|
|
100
|
+
facts.set(declaration.name.text, {
|
|
171
101
|
...fact,
|
|
172
|
-
sourceLine: lineOf(
|
|
102
|
+
sourceLine: lineOf(source, declaration),
|
|
173
103
|
});
|
|
174
104
|
}
|
|
105
|
+
}
|
|
175
106
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
107
|
+
return facts;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function exportedHelperFacts(
|
|
111
|
+
source: ts.SourceFile,
|
|
112
|
+
filePath: string,
|
|
113
|
+
): HelperBinding[] {
|
|
114
|
+
const facts = localHelperFacts(source);
|
|
115
|
+
const output: HelperBinding[] = [];
|
|
116
|
+
for (const [exportedName, localName] of exportedLocalNames(source))
|
|
117
|
+
for (const [key, fact] of facts) {
|
|
118
|
+
const [factLocal, returnedProperty] = key.split('#');
|
|
119
|
+
if (factLocal !== localName) continue;
|
|
120
|
+
output.push({
|
|
180
121
|
...fact,
|
|
181
122
|
exportedName,
|
|
123
|
+
returnedProperty,
|
|
182
124
|
sourceFile: normalizePath(filePath),
|
|
183
|
-
sourceLine: fact.sourceLine,
|
|
184
125
|
});
|
|
185
|
-
}
|
|
186
|
-
for (const [key, fact] of factsByLocal) {
|
|
187
|
-
const [localName, returnedProperty] = key.split('#');
|
|
188
|
-
if (!returnedProperty) continue;
|
|
189
|
-
for (const [exportedName, exportedLocal] of exportedLocals) {
|
|
190
|
-
if (exportedLocal !== localName) continue;
|
|
191
|
-
out.push({ ...fact, exportedName, returnedProperty, sourceFile: normalizePath(filePath), sourceLine: fact.sourceLine });
|
|
192
126
|
}
|
|
193
|
-
|
|
194
|
-
return out;
|
|
127
|
+
return output;
|
|
195
128
|
}
|
|
196
129
|
|
|
197
|
-
|
|
130
|
+
async function helperBindings(
|
|
198
131
|
repoPath: string,
|
|
199
132
|
filePath: string,
|
|
200
133
|
context?: RepositorySourceContext,
|
|
201
|
-
): Promise<
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const helperCache = new Map<string, HelperBinding[]>();
|
|
208
|
-
const classHelpers = collectClassHelpers(sourceFileAst);
|
|
209
|
-
const localObjectHelpers = new Map<string, HelperBinding[]>();
|
|
210
|
-
const localDirectHelpers = new Map<string, HelperBinding>();
|
|
211
|
-
const objectHelperVariables = new Map<string, Array<{ helper: HelperBinding; imp?: ImportBinding }>>();
|
|
212
|
-
for (const stmt of sourceFileAst.statements) {
|
|
213
|
-
if (ts.isFunctionDeclaration(stmt) && stmt.name) {
|
|
214
|
-
const directFact = directConnectFactFromFunctionLike(stmt);
|
|
215
|
-
if (directFact) localDirectHelpers.set(stmt.name.text, { ...directFact, exportedName: stmt.name.text, sourceFile: normalizePath(filePath), sourceLine: lineOf(sourceFileAst, stmt) });
|
|
216
|
-
const rows: HelperBinding[] = [];
|
|
217
|
-
for (const [returnedProperty, fact] of collectReturnedObjectBindings(stmt))
|
|
218
|
-
rows.push({ ...fact, exportedName: stmt.name.text, returnedProperty, sourceFile: normalizePath(filePath), sourceLine: lineOf(sourceFileAst, stmt) });
|
|
219
|
-
if (rows.length > 0) localObjectHelpers.set(stmt.name.text, rows);
|
|
220
|
-
}
|
|
221
|
-
if (ts.isVariableStatement(stmt)) {
|
|
222
|
-
for (const decl of stmt.declarationList.declarations) {
|
|
223
|
-
if (!ts.isIdentifier(decl.name)) continue;
|
|
224
|
-
const helper = functionLikeInitializer(decl.initializer);
|
|
225
|
-
if (!helper) continue;
|
|
226
|
-
const directFact = directConnectFactFromFunctionLike(helper);
|
|
227
|
-
if (directFact) localDirectHelpers.set(decl.name.text, { ...directFact, exportedName: decl.name.text, sourceFile: normalizePath(filePath), sourceLine: lineOf(sourceFileAst, decl) });
|
|
228
|
-
const rows: HelperBinding[] = [];
|
|
229
|
-
for (const [returnedProperty, fact] of collectReturnedObjectBindings(helper))
|
|
230
|
-
rows.push({ ...fact, exportedName: decl.name.text, returnedProperty, sourceFile: normalizePath(filePath), sourceLine: lineOf(sourceFileAst, decl) });
|
|
231
|
-
if (rows.length > 0) localObjectHelpers.set(decl.name.text, rows);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async function importedHelpers(
|
|
236
|
-
localName: string,
|
|
237
|
-
): Promise<Array<{ imp: ImportBinding; helper: HelperBinding }>> {
|
|
238
|
-
const imp = imports.find((i) => i.localName === localName && i.sourceFile);
|
|
239
|
-
if (!imp?.sourceFile) return [];
|
|
240
|
-
if (!helperCache.has(imp.sourceFile))
|
|
241
|
-
helperCache.set(
|
|
242
|
-
imp.sourceFile,
|
|
243
|
-
await helperBindings(repoPath, imp.sourceFile, context),
|
|
244
|
-
);
|
|
245
|
-
return (helperCache.get(imp.sourceFile) ?? [])
|
|
246
|
-
.filter((h) => h.exportedName === imp.exportedName)
|
|
247
|
-
.map((helper) => ({ imp, helper }));
|
|
248
|
-
}
|
|
249
|
-
async function importedHelper(
|
|
250
|
-
localName: string,
|
|
251
|
-
): Promise<{ imp: ImportBinding; helper: HelperBinding } | undefined> {
|
|
252
|
-
return (await importedHelpers(localName)).find((row) => !row.helper.returnedProperty);
|
|
253
|
-
}
|
|
254
|
-
function bindingForVariable(variableName: string): ServiceBindingFact | undefined {
|
|
255
|
-
const sourceFile = normalizePath(filePath);
|
|
256
|
-
return [...out]
|
|
257
|
-
.reverse()
|
|
258
|
-
.find((row) => row.variableName === variableName && row.sourceFile === sourceFile);
|
|
259
|
-
}
|
|
260
|
-
function cloneAliasBinding(targetName: string, sourceName: string, aliasKind: 'identity' | 'identity-assignment' | 'transaction', node: ts.Node): void {
|
|
261
|
-
const existing = bindingForVariable(sourceName);
|
|
262
|
-
if (!existing) return;
|
|
263
|
-
out.push({
|
|
264
|
-
...existing,
|
|
265
|
-
variableName: targetName,
|
|
266
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
267
|
-
helperChain: [
|
|
268
|
-
...(existing.helperChain ?? []),
|
|
269
|
-
{
|
|
270
|
-
callerVariable: targetName,
|
|
271
|
-
aliasOf: sourceName,
|
|
272
|
-
aliasKind,
|
|
273
|
-
scopeRule: 'same-file-source-order',
|
|
274
|
-
...(aliasKind === 'transaction' ? { transactionAliasSource: sourceName } : {}),
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
function recordIdentityAlias(decl: ts.VariableDeclaration): void {
|
|
280
|
-
if (!ts.isIdentifier(decl.name) || !decl.initializer) return;
|
|
281
|
-
const unwrapped = unwrapIdentityExpression(decl.initializer);
|
|
282
|
-
if (!ts.isIdentifier(unwrapped)) return;
|
|
283
|
-
cloneAliasBinding(decl.name.text, unwrapped.text, 'identity', decl);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async function recordBindingFromExpression(targetName: string, expr: ts.Expression, node: ts.Node, aliasKind: 'declaration' | 'assignment'): Promise<void> {
|
|
287
|
-
const call = unwrapCall(expr);
|
|
288
|
-
if (!call) return;
|
|
289
|
-
const direct = connectFactFromCall(call);
|
|
290
|
-
if (direct)
|
|
291
|
-
out.push({
|
|
292
|
-
variableName: targetName,
|
|
293
|
-
...direct,
|
|
294
|
-
sourceFile: normalizePath(filePath),
|
|
295
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
296
|
-
helperChain: aliasKind === 'assignment'
|
|
297
|
-
? [{ callerVariable: targetName, assignedFrom: call.expression.getText(sourceFileAst), aliasKind, scopeRule: 'same-file-source-order' }]
|
|
298
|
-
: undefined,
|
|
299
|
-
});
|
|
300
|
-
else if (ts.isIdentifier(call.expression)) {
|
|
301
|
-
const localDirect = localDirectHelpers.get(call.expression.text);
|
|
302
|
-
const resolved = localDirect ? { helper: localDirect, imp: undefined } : await importedHelper(call.expression.text);
|
|
303
|
-
if (resolved)
|
|
304
|
-
out.push({
|
|
305
|
-
variableName: targetName,
|
|
306
|
-
alias: resolved.helper.alias,
|
|
307
|
-
aliasExpr: resolved.helper.aliasExpr,
|
|
308
|
-
destinationExpr: resolved.helper.destinationExpr,
|
|
309
|
-
servicePathExpr: resolved.helper.servicePathExpr,
|
|
310
|
-
isDynamic: resolved.helper.isDynamic,
|
|
311
|
-
placeholders: resolved.helper.placeholders,
|
|
312
|
-
sourceFile: normalizePath(filePath),
|
|
313
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
314
|
-
helperChain: [
|
|
315
|
-
...(resolved.helper.helperChain ?? []),
|
|
316
|
-
{
|
|
317
|
-
callerVariable: targetName,
|
|
318
|
-
...(aliasKind === 'assignment' ? { assignedFrom: call.expression.text, aliasKind, scopeRule: 'same-file-source-order' } : {}),
|
|
319
|
-
importedHelper: call.expression.text,
|
|
320
|
-
importSource: resolved.imp?.sourceFile,
|
|
321
|
-
exportedSymbol: resolved.imp?.exportedName ?? resolved.helper.exportedName,
|
|
322
|
-
helperSourceFile: resolved.helper.sourceFile,
|
|
323
|
-
helperSourceLine: resolved.helper.sourceLine,
|
|
324
|
-
},
|
|
325
|
-
],
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
async function recordVariable(decl: ts.VariableDeclaration): Promise<void> {
|
|
330
|
-
if (!ts.isIdentifier(decl.name) || !decl.initializer) return;
|
|
331
|
-
await recordBindingFromExpression(decl.name.text, decl.initializer, decl, 'declaration');
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
async function helpersForCall(call: ts.CallExpression): Promise<Array<{ helper: HelperBinding; imp?: ImportBinding }>> {
|
|
335
|
-
if (!ts.isIdentifier(call.expression)) return [];
|
|
336
|
-
const local = localObjectHelpers.get(call.expression.text) ?? [];
|
|
337
|
-
const imported = await importedHelpers(call.expression.text);
|
|
338
|
-
return [...local.map((helper) => ({ helper })), ...imported];
|
|
339
|
-
}
|
|
340
|
-
async function rememberObjectHelperVariable(decl: ts.VariableDeclaration): Promise<void> {
|
|
341
|
-
if (!ts.isIdentifier(decl.name) || !decl.initializer) return;
|
|
342
|
-
const call = unwrapCall(decl.initializer);
|
|
343
|
-
if (!call) return;
|
|
344
|
-
const helpers = (await helpersForCall(call)).filter((row) => row.helper.returnedProperty);
|
|
345
|
-
if (helpers.length > 0) objectHelperVariables.set(decl.name.text, helpers);
|
|
346
|
-
}
|
|
347
|
-
function recordObjectPropertyBinding(targetName: string, expr: ts.Expression, node: ts.Node): boolean {
|
|
348
|
-
const unwrapped = unwrapIdentityExpression(expr);
|
|
349
|
-
if (!ts.isPropertyAccessExpression(unwrapped) || !ts.isIdentifier(unwrapped.expression)) return false;
|
|
350
|
-
const helpers = objectHelperVariables.get(unwrapped.expression.text) ?? [];
|
|
351
|
-
const matches = helpers.filter((row) => row.helper.returnedProperty === unwrapped.name.text);
|
|
352
|
-
if (matches.length !== 1) return false;
|
|
353
|
-
const resolved = matches[0];
|
|
354
|
-
out.push({
|
|
355
|
-
variableName: targetName,
|
|
356
|
-
alias: resolved.helper.alias,
|
|
357
|
-
aliasExpr: resolved.helper.aliasExpr,
|
|
358
|
-
destinationExpr: resolved.helper.destinationExpr,
|
|
359
|
-
servicePathExpr: resolved.helper.servicePathExpr,
|
|
360
|
-
isDynamic: resolved.helper.isDynamic,
|
|
361
|
-
placeholders: resolved.helper.placeholders,
|
|
362
|
-
sourceFile: normalizePath(filePath),
|
|
363
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
364
|
-
helperChain: [
|
|
365
|
-
...(resolved.helper.helperChain ?? []),
|
|
366
|
-
{
|
|
367
|
-
callerVariable: targetName,
|
|
368
|
-
sourceVariable: unwrapped.expression.text,
|
|
369
|
-
returnedProperty: unwrapped.name.text,
|
|
370
|
-
assignedFromProperty: unwrapped.getText(sourceFileAst),
|
|
371
|
-
importSource: resolved.imp?.sourceFile,
|
|
372
|
-
exportedSymbol: resolved.imp?.exportedName,
|
|
373
|
-
helperSourceFile: resolved.helper.sourceFile,
|
|
374
|
-
helperSourceLine: resolved.helper.sourceLine,
|
|
375
|
-
},
|
|
376
|
-
],
|
|
377
|
-
});
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
async function recordDestructuredHelper(decl: ts.VariableDeclaration): Promise<void> {
|
|
381
|
-
if (!ts.isObjectBindingPattern(decl.name) || !decl.initializer) return;
|
|
382
|
-
const call = unwrapCall(decl.initializer);
|
|
383
|
-
if (!call) return;
|
|
384
|
-
const helpers = await helpersForCall(call);
|
|
385
|
-
if (helpers.length === 0) return;
|
|
386
|
-
for (const el of decl.name.elements) {
|
|
387
|
-
if (!ts.isIdentifier(el.name)) continue;
|
|
388
|
-
const propertyName = el.propertyName && ts.isIdentifier(el.propertyName) ? el.propertyName.text : el.name.text;
|
|
389
|
-
const matches = helpers.filter((row) => row.helper.returnedProperty === propertyName);
|
|
390
|
-
if (matches.length !== 1) continue;
|
|
391
|
-
const resolved = matches[0];
|
|
392
|
-
out.push({
|
|
393
|
-
variableName: el.name.text,
|
|
394
|
-
alias: resolved.helper.alias,
|
|
395
|
-
aliasExpr: resolved.helper.aliasExpr,
|
|
396
|
-
destinationExpr: resolved.helper.destinationExpr,
|
|
397
|
-
servicePathExpr: resolved.helper.servicePathExpr,
|
|
398
|
-
isDynamic: resolved.helper.isDynamic,
|
|
399
|
-
placeholders: resolved.helper.placeholders,
|
|
400
|
-
sourceFile: normalizePath(filePath),
|
|
401
|
-
sourceLine: lineOf(sourceFileAst, decl),
|
|
402
|
-
helperChain: [...(resolved.helper.helperChain ?? []), { callerVariable: el.name.text, helperFunction: call.expression.getText(sourceFileAst), returnedProperty: propertyName, importSource: resolved.imp?.sourceFile, exportedSymbol: resolved.imp?.exportedName, helperSourceFile: resolved.helper.sourceFile, helperSourceLine: resolved.helper.sourceLine }],
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
async function recordDestructuredAssignment(pattern: ts.ObjectLiteralExpression, expr: ts.Expression, node: ts.Node): Promise<void> {
|
|
407
|
-
const call = unwrapCall(expr);
|
|
408
|
-
if (!call) return;
|
|
409
|
-
const helpers = await helpersForCall(call);
|
|
410
|
-
if (helpers.length === 0) return;
|
|
411
|
-
for (const prop of pattern.properties) {
|
|
412
|
-
let propertyName: string | undefined;
|
|
413
|
-
let targetName: string | undefined;
|
|
414
|
-
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
415
|
-
propertyName = prop.name.text;
|
|
416
|
-
targetName = prop.name.text;
|
|
417
|
-
} else if (ts.isPropertyAssignment(prop)) {
|
|
418
|
-
propertyName = ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name) ? prop.name.text : undefined;
|
|
419
|
-
targetName = ts.isIdentifier(prop.initializer) ? prop.initializer.text : undefined;
|
|
420
|
-
}
|
|
421
|
-
if (!propertyName || !targetName) continue;
|
|
422
|
-
const matches = helpers.filter((row) => row.helper.returnedProperty === propertyName);
|
|
423
|
-
if (matches.length !== 1) continue;
|
|
424
|
-
const resolved = matches[0];
|
|
425
|
-
out.push({
|
|
426
|
-
variableName: targetName,
|
|
427
|
-
alias: resolved.helper.alias,
|
|
428
|
-
aliasExpr: resolved.helper.aliasExpr,
|
|
429
|
-
destinationExpr: resolved.helper.destinationExpr,
|
|
430
|
-
servicePathExpr: resolved.helper.servicePathExpr,
|
|
431
|
-
isDynamic: resolved.helper.isDynamic,
|
|
432
|
-
placeholders: resolved.helper.placeholders,
|
|
433
|
-
sourceFile: normalizePath(filePath),
|
|
434
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
435
|
-
helperChain: [...(resolved.helper.helperChain ?? []), { callerVariable: targetName, assignedFrom: call.expression.getText(sourceFileAst), aliasKind: 'assignment', scopeRule: 'same-file-source-order', returnedProperty: propertyName, importSource: resolved.imp?.sourceFile, exportedSymbol: resolved.imp?.exportedName, helperSourceFile: resolved.helper.sourceFile, helperSourceLine: resolved.helper.sourceLine }],
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
function recordDestructuredClassHelper(decl: ts.VariableDeclaration): void {
|
|
440
|
-
if (!ts.isObjectBindingPattern(decl.name) || !decl.initializer) return;
|
|
441
|
-
const call = unwrapCall(decl.initializer);
|
|
442
|
-
if (!call || !ts.isPropertyAccessExpression(call.expression)) return;
|
|
443
|
-
const target = call.expression;
|
|
444
|
-
if (target.expression.kind !== ts.SyntaxKind.ThisKeyword) return;
|
|
445
|
-
for (const el of decl.name.elements) {
|
|
446
|
-
if (!ts.isIdentifier(el.name)) continue;
|
|
447
|
-
const propertyName = el.propertyName && ts.isIdentifier(el.propertyName)
|
|
448
|
-
? el.propertyName.text
|
|
449
|
-
: el.name.text;
|
|
450
|
-
const helper = classHelpers.find(
|
|
451
|
-
(h) => h.helperName === target.name.text && h.propertyName === propertyName,
|
|
452
|
-
);
|
|
453
|
-
if (!helper) continue;
|
|
454
|
-
out.push({
|
|
455
|
-
variableName: el.name.text,
|
|
456
|
-
...helper.fact,
|
|
457
|
-
sourceFile: normalizePath(filePath),
|
|
458
|
-
sourceLine: lineOf(sourceFileAst, decl),
|
|
459
|
-
helperChain: [
|
|
460
|
-
{
|
|
461
|
-
callerVariable: el.name.text,
|
|
462
|
-
className: helper.className,
|
|
463
|
-
classHelper: helper.helperName,
|
|
464
|
-
returnedProperty: helper.propertyName,
|
|
465
|
-
helperVariable: helper.variableName,
|
|
466
|
-
helperSourceFile: normalizePath(filePath),
|
|
467
|
-
helperSourceLine: helper.sourceLine,
|
|
468
|
-
},
|
|
469
|
-
],
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function arrayElementsFromExpression(expr: ts.Expression): { elements: ts.NodeArray<ts.Expression>; promiseAll: boolean } | undefined {
|
|
475
|
-
const unwrapped = unwrapIdentityExpression(expr);
|
|
476
|
-
if (ts.isArrayLiteralExpression(unwrapped)) return { elements: unwrapped.elements, promiseAll: false };
|
|
477
|
-
const call = unwrapCall(expr);
|
|
478
|
-
if (!call) return undefined;
|
|
479
|
-
if (!ts.isPropertyAccessExpression(call.expression) || call.expression.name.text !== 'all' || call.expression.expression.getText(sourceFileAst) !== 'Promise') return undefined;
|
|
480
|
-
const first = call.arguments[0];
|
|
481
|
-
if (!first) return undefined;
|
|
482
|
-
const container = unwrapIdentityExpression(first);
|
|
483
|
-
if (!ts.isArrayLiteralExpression(container)) return undefined;
|
|
484
|
-
return { elements: container.elements, promiseAll: true };
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async function recordArrayElementBinding(targetName: string, expr: ts.Expression, node: ts.Node, arrayIndex: number, promiseAll: boolean): Promise<void> {
|
|
488
|
-
const before = out.length;
|
|
489
|
-
await recordBindingFromExpression(targetName, expr, node, 'declaration');
|
|
490
|
-
if (out.length > before) {
|
|
491
|
-
const row = out[out.length - 1];
|
|
492
|
-
row.helperChain = [
|
|
493
|
-
...(row.helperChain ?? []),
|
|
494
|
-
{ callerVariable: targetName, targetVariable: targetName, arrayIndex, promiseAll, arrayContainer: promiseAll ? 'Promise.all' : 'array_literal' },
|
|
495
|
-
];
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
const unwrapped = unwrapIdentityExpression(expr);
|
|
499
|
-
if (ts.isIdentifier(unwrapped)) {
|
|
500
|
-
const existing = bindingForVariable(unwrapped.text);
|
|
501
|
-
if (!existing) return;
|
|
502
|
-
out.push({
|
|
503
|
-
...existing,
|
|
504
|
-
variableName: targetName,
|
|
505
|
-
sourceLine: lineOf(sourceFileAst, node),
|
|
506
|
-
helperChain: [
|
|
507
|
-
...(existing.helperChain ?? []),
|
|
508
|
-
{ callerVariable: targetName, targetVariable: targetName, sourceVariable: unwrapped.text, aliasKind: 'array-destructuring', arrayIndex, promiseAll, arrayContainer: promiseAll ? 'Promise.all' : 'array_literal' },
|
|
509
|
-
],
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
async function recordArrayDestructuredVariable(decl: ts.VariableDeclaration): Promise<void> {
|
|
515
|
-
if (!ts.isArrayBindingPattern(decl.name) || !decl.initializer) return;
|
|
516
|
-
const container = arrayElementsFromExpression(decl.initializer);
|
|
517
|
-
if (!container) return;
|
|
518
|
-
for (let index = 0; index < decl.name.elements.length; index += 1) {
|
|
519
|
-
const el = decl.name.elements[index];
|
|
520
|
-
if (!el || ts.isOmittedExpression(el) || ts.isBindingElement(el) && el.dotDotDotToken) continue;
|
|
521
|
-
if (!ts.isBindingElement(el) || !ts.isIdentifier(el.name)) continue;
|
|
522
|
-
const source = container.elements[index];
|
|
523
|
-
if (!source || ts.isOmittedExpression(source)) continue;
|
|
524
|
-
await recordArrayElementBinding(el.name.text, source, decl, index, container.promiseAll);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async function recordArrayDestructuredAssignment(pattern: ts.ArrayLiteralExpression, expr: ts.Expression, node: ts.Node): Promise<void> {
|
|
529
|
-
const container = arrayElementsFromExpression(expr);
|
|
530
|
-
if (!container) return;
|
|
531
|
-
for (let index = 0; index < pattern.elements.length; index += 1) {
|
|
532
|
-
const el = pattern.elements[index];
|
|
533
|
-
if (!el || ts.isOmittedExpression(el) || ts.isSpreadElement(el) || !ts.isIdentifier(el)) continue;
|
|
534
|
-
const source = container.elements[index];
|
|
535
|
-
if (!source || ts.isOmittedExpression(source)) continue;
|
|
536
|
-
await recordArrayElementBinding(el.text, source, node, index, container.promiseAll);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
134
|
+
): Promise<HelperBinding[]> {
|
|
135
|
+
const source = await readSource(
|
|
136
|
+
path.join(repoPath, filePath), context, filePath,
|
|
137
|
+
);
|
|
138
|
+
return source ? exportedHelperFacts(source, filePath) : [];
|
|
139
|
+
}
|
|
539
140
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
if (ts.isIdentifier(decl.name) && decl.initializer) recordObjectPropertyBinding(decl.name.text, decl.initializer, decl);
|
|
557
|
-
await recordVariable(decl);
|
|
558
|
-
recordIdentityAlias(decl);
|
|
559
|
-
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
560
|
-
const sourceName = transactionReceiverName(decl.initializer);
|
|
561
|
-
if (sourceName) cloneAliasBinding(decl.name.text, sourceName, 'transaction', decl);
|
|
562
|
-
}
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
const assignment = event.node;
|
|
566
|
-
if (ts.isIdentifier(assignment.left)) {
|
|
567
|
-
const rhs = unwrapIdentityExpression(assignment.right);
|
|
568
|
-
if (ts.isIdentifier(rhs)) {
|
|
569
|
-
cloneAliasBinding(assignment.left.text, rhs.text, 'identity-assignment', assignment);
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
if (recordObjectPropertyBinding(assignment.left.text, assignment.right, assignment)) continue;
|
|
573
|
-
await recordBindingFromExpression(assignment.left.text, assignment.right, assignment, 'assignment');
|
|
574
|
-
continue;
|
|
575
|
-
}
|
|
576
|
-
const left = ts.isParenthesizedExpression(assignment.left) ? assignment.left.expression : assignment.left;
|
|
577
|
-
if (ts.isObjectLiteralExpression(left))
|
|
578
|
-
await recordDestructuredAssignment(left, assignment.right, assignment);
|
|
579
|
-
if (ts.isArrayLiteralExpression(left))
|
|
580
|
-
await recordArrayDestructuredAssignment(left, assignment.right, assignment);
|
|
581
|
-
}
|
|
582
|
-
return out;
|
|
141
|
+
function classHelperFacts(
|
|
142
|
+
source: ts.SourceFile,
|
|
143
|
+
className: string,
|
|
144
|
+
helperName: string,
|
|
145
|
+
helper: ts.FunctionLikeDeclaration,
|
|
146
|
+
): ClassHelperReturn[] {
|
|
147
|
+
return [...collectReturnedObjectBindings(helper)].map(
|
|
148
|
+
([propertyName, fact]) => ({
|
|
149
|
+
className,
|
|
150
|
+
helperName,
|
|
151
|
+
propertyName,
|
|
152
|
+
variableName: propertyName,
|
|
153
|
+
fact,
|
|
154
|
+
sourceLine: lineOf(source, helper),
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
583
157
|
}
|
|
584
158
|
|
|
585
|
-
function collectClassHelpers(
|
|
159
|
+
function collectClassHelpers(source: ts.SourceFile): ClassHelperReturn[] {
|
|
586
160
|
const helpers: ClassHelperReturn[] = [];
|
|
587
|
-
for (const
|
|
588
|
-
if (!ts.isClassDeclaration(
|
|
589
|
-
for (const member of
|
|
590
|
-
if (!ts.isPropertyDeclaration(member)
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
string,
|
|
599
|
-
Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>
|
|
600
|
-
>();
|
|
601
|
-
function visit(node: ts.Node): void {
|
|
602
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
603
|
-
const fact = findConnectInExpression(node.initializer);
|
|
604
|
-
if (fact) bindings.set(node.name.text, fact);
|
|
605
|
-
}
|
|
606
|
-
if (ts.isReturnStatement(node) && node.expression && ts.isObjectLiteralExpression(node.expression)) {
|
|
607
|
-
for (const prop of node.expression.properties) {
|
|
608
|
-
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
609
|
-
const fact = bindings.get(prop.name.text);
|
|
610
|
-
if (fact)
|
|
611
|
-
helpers.push({
|
|
612
|
-
className,
|
|
613
|
-
helperName,
|
|
614
|
-
propertyName: prop.name.text,
|
|
615
|
-
variableName: prop.name.text,
|
|
616
|
-
fact,
|
|
617
|
-
sourceLine: lineOf(sf, prop),
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.initializer)) {
|
|
621
|
-
const propertyName =
|
|
622
|
-
ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name)
|
|
623
|
-
? prop.name.text
|
|
624
|
-
: undefined;
|
|
625
|
-
const fact = propertyName ? bindings.get(prop.initializer.text) : undefined;
|
|
626
|
-
if (propertyName && fact)
|
|
627
|
-
helpers.push({
|
|
628
|
-
className,
|
|
629
|
-
helperName,
|
|
630
|
-
propertyName,
|
|
631
|
-
variableName: prop.initializer.text,
|
|
632
|
-
fact,
|
|
633
|
-
sourceLine: lineOf(sf, prop),
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
ts.forEachChild(node, visit);
|
|
639
|
-
}
|
|
640
|
-
visit(initializer);
|
|
161
|
+
for (const statement of source.statements) {
|
|
162
|
+
if (!ts.isClassDeclaration(statement) || !statement.name) continue;
|
|
163
|
+
for (const member of statement.members) {
|
|
164
|
+
if (!ts.isPropertyDeclaration(member)
|
|
165
|
+
|| !ts.isIdentifier(member.name)
|
|
166
|
+
|| !member.initializer) continue;
|
|
167
|
+
const helper = functionLikeInitializer(member.initializer);
|
|
168
|
+
if (helper)
|
|
169
|
+
helpers.push(...classHelperFacts(
|
|
170
|
+
source, statement.name.text, member.name.text, helper,
|
|
171
|
+
));
|
|
641
172
|
}
|
|
642
173
|
}
|
|
643
174
|
return helpers;
|
|
644
175
|
}
|
|
176
|
+
|
|
177
|
+
export async function parseServiceBindings(
|
|
178
|
+
repoPath: string,
|
|
179
|
+
filePath: string,
|
|
180
|
+
context?: RepositorySourceContext,
|
|
181
|
+
): Promise<ServiceBindingFact[]> {
|
|
182
|
+
const source = await readSource(
|
|
183
|
+
path.join(repoPath, filePath), context, filePath,
|
|
184
|
+
);
|
|
185
|
+
if (!source) return [];
|
|
186
|
+
return collectServiceBindings({
|
|
187
|
+
source,
|
|
188
|
+
filePath,
|
|
189
|
+
imports: await importsFor(repoPath, filePath, source),
|
|
190
|
+
classHelpers: collectClassHelpers(source),
|
|
191
|
+
loadHelperBindings: async (helperFile) =>
|
|
192
|
+
helperBindings(repoPath, helperFile, context),
|
|
193
|
+
});
|
|
194
|
+
}
|