@saptools/service-flow 0.1.70 → 0.1.72
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 +13 -0
- package/README.md +9 -5
- package/TECHNICAL-NOTE.md +13 -0
- package/dist/{chunk-GSLFY6J2.js → chunk-Z6D433R5.js} +8696 -6476
- package/dist/chunk-Z6D433R5.js.map +1 -0
- package/dist/cli.js +685 -142
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +53 -14
- package/dist/index.js +2 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/doctor-event-quality.ts +371 -0
- package/src/cli/doctor.ts +4 -6
- package/src/cli.ts +1 -1
- package/src/db/call-fact-repository.ts +6 -3
- package/src/db/current-fact-semantics.ts +5 -5
- package/src/db/event-fact-semantics.ts +347 -0
- package/src/db/event-surface-invalidation.ts +38 -0
- package/src/db/fact-json-inventory.ts +16 -0
- package/src/db/migrations.ts +12 -1
- package/src/db/package-target-invalidation.ts +79 -0
- package/src/db/repositories.ts +28 -2
- package/src/db/schema-structure.ts +41 -1
- package/src/db/schema.ts +6 -2
- package/src/indexer/repository-indexer.ts +45 -6
- package/src/linker/cross-repo-linker.ts +25 -3
- package/src/linker/event-environment-link.ts +211 -0
- package/src/linker/event-shape-candidate-linker.ts +161 -0
- package/src/linker/event-subscription-handler-linker.ts +123 -19
- package/src/linker/event-template-link.ts +40 -6
- package/src/linker/package-event-constant-resolver.ts +298 -0
- package/src/output/table-output.ts +13 -1
- package/src/parsers/decorator-parser.ts +9 -53
- package/src/parsers/environment-declarations.ts +327 -0
- package/src/parsers/event-call-analysis.ts +242 -0
- package/src/parsers/event-environment-reference.ts +231 -0
- package/src/parsers/event-loop-registration.ts +132 -0
- package/src/parsers/event-name-import-resolution.ts +243 -0
- package/src/parsers/event-receiver-analysis.ts +404 -0
- package/src/parsers/event-subscription-facts.ts +4 -0
- package/src/parsers/generated-constants-parser.ts +80 -14
- package/src/parsers/outbound-call-classifier.ts +27 -124
- package/src/parsers/outbound-call-parser.ts +13 -1
- package/src/parsers/outbound-expression-analysis.ts +2 -2
- package/src/parsers/stable-local-value.ts +30 -9
- package/src/parsers/string-constant-lookups.ts +358 -0
- package/src/trace/event-runtime-resolution.ts +24 -3
- package/src/trace/event-shape-candidate-trace.ts +172 -0
- package/src/trace/event-subscriber-traversal.ts +19 -7
- package/src/trace/evidence.ts +7 -0
- package/src/trace/trace-scope-execution.ts +21 -29
- package/src/types.ts +17 -1
- package/src/utils/event-skeleton.ts +207 -0
- package/src/version.ts +1 -1
- package/dist/chunk-GSLFY6J2.js.map +0 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import type { Db } from '../db/connection.js';
|
|
2
|
+
import {
|
|
3
|
+
parsePackageImportReference,
|
|
4
|
+
parsePackagePublicSurfaceFact,
|
|
5
|
+
} from '../parsers/package-fact-contract.js';
|
|
6
|
+
import type {
|
|
7
|
+
PackagePublicSurfaceFact,
|
|
8
|
+
} from '../parsers/package-public-surface.js';
|
|
9
|
+
import type {
|
|
10
|
+
SymbolImportReference,
|
|
11
|
+
} from '../parsers/symbol-import-bindings.js';
|
|
12
|
+
|
|
13
|
+
export interface PackageEventConstantLinkSummary {
|
|
14
|
+
resolved: number;
|
|
15
|
+
ambiguous: number;
|
|
16
|
+
unresolved: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface EventConstantCall {
|
|
20
|
+
id: number;
|
|
21
|
+
binding: SymbolImportReference;
|
|
22
|
+
evidence: Record<string, unknown>;
|
|
23
|
+
unresolvedReason?: string | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PackageRepository {
|
|
27
|
+
id: number;
|
|
28
|
+
surface: PackagePublicSurfaceFact;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PackageEventConstantResolution {
|
|
32
|
+
status: 'resolved' | 'ambiguous' | 'unresolved';
|
|
33
|
+
reason?: string;
|
|
34
|
+
value?: string;
|
|
35
|
+
sourceFile?: string;
|
|
36
|
+
sourceLine?: number;
|
|
37
|
+
targetRepoId?: number;
|
|
38
|
+
modulePath?: string;
|
|
39
|
+
candidateCount: number;
|
|
40
|
+
eligibleCandidateCount: number;
|
|
41
|
+
complete: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
45
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
46
|
+
? value as Record<string, unknown> : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function jsonRecord(value: unknown): Record<string, unknown> | undefined {
|
|
50
|
+
if (typeof value !== 'string') return undefined;
|
|
51
|
+
try {
|
|
52
|
+
return record(JSON.parse(value) as unknown);
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function callRows(db: Db, workspaceId: number): EventConstantCall[] {
|
|
59
|
+
const rows = db.prepare(`SELECT c.id,c.evidence_json evidenceJson,
|
|
60
|
+
c.unresolved_reason unresolvedReason
|
|
61
|
+
FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
|
|
62
|
+
WHERE r.workspace_id=?
|
|
63
|
+
AND c.call_type IN ('async_emit','async_subscribe')
|
|
64
|
+
AND json_extract(c.evidence_json,
|
|
65
|
+
'$.eventNameConstantImportBinding.moduleKind')='package'
|
|
66
|
+
ORDER BY c.id`).all(workspaceId);
|
|
67
|
+
return rows.flatMap((row) => {
|
|
68
|
+
const evidence = jsonRecord(row.evidenceJson);
|
|
69
|
+
const binding = parsePackageImportReference(
|
|
70
|
+
evidence?.eventNameConstantImportBinding,
|
|
71
|
+
);
|
|
72
|
+
return typeof row.id === 'number' && evidence && binding ? [{
|
|
73
|
+
id: row.id, evidence, binding,
|
|
74
|
+
unresolvedReason: typeof row.unresolvedReason === 'string'
|
|
75
|
+
? row.unresolvedReason : null,
|
|
76
|
+
}] : [];
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseSurface(
|
|
81
|
+
value: unknown,
|
|
82
|
+
packageName: string,
|
|
83
|
+
): PackagePublicSurfaceFact | undefined {
|
|
84
|
+
if (typeof value !== 'string') return undefined;
|
|
85
|
+
try {
|
|
86
|
+
return parsePackagePublicSurfaceFact(
|
|
87
|
+
JSON.parse(value) as unknown, packageName,
|
|
88
|
+
);
|
|
89
|
+
} catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function repositories(
|
|
95
|
+
db: Db,
|
|
96
|
+
workspaceId: number,
|
|
97
|
+
packageName: string,
|
|
98
|
+
): PackageRepository[] {
|
|
99
|
+
return db.prepare(`SELECT id,package_public_surface_json surfaceJson
|
|
100
|
+
FROM repositories WHERE workspace_id=? AND package_name=?
|
|
101
|
+
ORDER BY id`).all(workspaceId, packageName).flatMap((row) => {
|
|
102
|
+
const surface = parseSurface(row.surfaceJson, packageName);
|
|
103
|
+
return typeof row.id === 'number' && surface
|
|
104
|
+
? [{ id: row.id, surface }] : [];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function failure(
|
|
109
|
+
reason: string,
|
|
110
|
+
fields: Partial<PackageEventConstantResolution> = {},
|
|
111
|
+
): PackageEventConstantResolution {
|
|
112
|
+
return {
|
|
113
|
+
status: reason === 'event_name_constant_container_ambiguous'
|
|
114
|
+
? 'ambiguous' : 'unresolved',
|
|
115
|
+
reason,
|
|
116
|
+
candidateCount: 0,
|
|
117
|
+
eligibleCandidateCount: 0,
|
|
118
|
+
complete: true,
|
|
119
|
+
...fields,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function entryModule(
|
|
124
|
+
repository: PackageRepository,
|
|
125
|
+
binding: SymbolImportReference,
|
|
126
|
+
): string | PackageEventConstantResolution {
|
|
127
|
+
const surface = repository.surface;
|
|
128
|
+
if (surface.status !== 'complete' || surface.omitted > 0) return failure(
|
|
129
|
+
'event_name_constant_container_ambiguous', { complete: false },
|
|
130
|
+
);
|
|
131
|
+
const matches = surface.entries.filter((entry) =>
|
|
132
|
+
entry.entry === binding.requestedModuleSubpath);
|
|
133
|
+
return matches.length === 1 && matches[0]
|
|
134
|
+
? matches[0].modulePath
|
|
135
|
+
: failure('event_name_constant_container_ambiguous', {
|
|
136
|
+
complete: matches.length === 0 && surface.omitted === 0,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function moduleFile(sourceFile: string): string {
|
|
141
|
+
return sourceFile.replace(/\.(?:d\.)?(?:ts|js)$/, '');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function constantRows(
|
|
145
|
+
db: Db,
|
|
146
|
+
repoId: number,
|
|
147
|
+
modulePath: string,
|
|
148
|
+
publicName: string,
|
|
149
|
+
): Array<Record<string, unknown>> {
|
|
150
|
+
return db.prepare(`SELECT id,source_file sourceFile,
|
|
151
|
+
source_line sourceLine,value,exported,stable,
|
|
152
|
+
resolution_status resolutionStatus,unresolved_reason unresolvedReason
|
|
153
|
+
FROM generated_constants
|
|
154
|
+
WHERE repo_id=? AND name=?
|
|
155
|
+
ORDER BY source_file COLLATE BINARY,declaration_start_offset,id`).all(
|
|
156
|
+
repoId, publicName,
|
|
157
|
+
).filter((row) =>
|
|
158
|
+
typeof row.sourceFile === 'string'
|
|
159
|
+
&& moduleFile(row.sourceFile) === modulePath);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function resolveRows(
|
|
163
|
+
rows: Array<Record<string, unknown>>,
|
|
164
|
+
repoId: number,
|
|
165
|
+
modulePath: string,
|
|
166
|
+
): PackageEventConstantResolution {
|
|
167
|
+
const fields = {
|
|
168
|
+
targetRepoId: repoId,
|
|
169
|
+
modulePath,
|
|
170
|
+
candidateCount: rows.length,
|
|
171
|
+
complete: true,
|
|
172
|
+
};
|
|
173
|
+
if (rows.length !== 1 || !rows[0]) return failure(
|
|
174
|
+
'event_name_constant_container_ambiguous',
|
|
175
|
+
fields,
|
|
176
|
+
);
|
|
177
|
+
const row = rows[0];
|
|
178
|
+
if (Number(row.exported) !== 1) return failure(
|
|
179
|
+
'event_name_constant_container_not_exported', fields,
|
|
180
|
+
);
|
|
181
|
+
if (Number(row.stable) !== 1) return failure(
|
|
182
|
+
'event_name_constant_container_mutable', fields,
|
|
183
|
+
);
|
|
184
|
+
if (row.resolutionStatus !== 'resolved'
|
|
185
|
+
|| typeof row.value !== 'string') return failure(
|
|
186
|
+
String(row.unresolvedReason) === 'event_name_constant_container_mutable'
|
|
187
|
+
? 'event_name_constant_container_mutable'
|
|
188
|
+
: 'event_name_constant_member_not_string',
|
|
189
|
+
fields,
|
|
190
|
+
);
|
|
191
|
+
return {
|
|
192
|
+
...fields,
|
|
193
|
+
status: 'resolved',
|
|
194
|
+
value: row.value,
|
|
195
|
+
sourceFile: String(row.sourceFile),
|
|
196
|
+
sourceLine: Number(row.sourceLine),
|
|
197
|
+
eligibleCandidateCount: 1,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function resolveCall(
|
|
202
|
+
db: Db,
|
|
203
|
+
workspaceId: number,
|
|
204
|
+
call: EventConstantCall,
|
|
205
|
+
): PackageEventConstantResolution {
|
|
206
|
+
const packageName = call.binding.requestedPackageName ?? '';
|
|
207
|
+
const matches = repositories(db, workspaceId, packageName);
|
|
208
|
+
if (matches.length !== 1 || !matches[0]) return failure(
|
|
209
|
+
'event_name_constant_container_ambiguous',
|
|
210
|
+
{ candidateCount: matches.length, complete: matches.length === 0 },
|
|
211
|
+
);
|
|
212
|
+
const module = entryModule(matches[0], call.binding);
|
|
213
|
+
if (typeof module !== 'string') return {
|
|
214
|
+
...module, targetRepoId: matches[0].id,
|
|
215
|
+
};
|
|
216
|
+
return resolveRows(
|
|
217
|
+
constantRows(
|
|
218
|
+
db, matches[0].id, module, call.binding.requestedPublicName,
|
|
219
|
+
),
|
|
220
|
+
matches[0].id,
|
|
221
|
+
module,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function expectedPackageEventConstantResolution(
|
|
226
|
+
db: Db,
|
|
227
|
+
workspaceId: number,
|
|
228
|
+
binding: SymbolImportReference,
|
|
229
|
+
): PackageEventConstantResolution {
|
|
230
|
+
return resolveCall(db, workspaceId, {
|
|
231
|
+
id: 0,
|
|
232
|
+
binding,
|
|
233
|
+
evidence: {},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function receiverReason(call: EventConstantCall): string | null {
|
|
238
|
+
return call.evidence.receiverClassification === 'unproven'
|
|
239
|
+
? call.unresolvedReason ?? 'event_receiver_unproven_binding' : null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function resolutionEvidence(
|
|
243
|
+
call: EventConstantCall,
|
|
244
|
+
resolution: PackageEventConstantResolution,
|
|
245
|
+
): string {
|
|
246
|
+
const evidence = { ...call.evidence };
|
|
247
|
+
delete evidence.eventNameUnresolvedReason;
|
|
248
|
+
delete evidence.eventNameStatus;
|
|
249
|
+
delete evidence.eventNameSourceKind;
|
|
250
|
+
delete evidence.eventNamePlaceholderKeys;
|
|
251
|
+
if (resolution.status !== 'resolved')
|
|
252
|
+
evidence.eventNameUnresolvedReason = resolution.reason;
|
|
253
|
+
return JSON.stringify({
|
|
254
|
+
...evidence,
|
|
255
|
+
...(resolution.status === 'resolved' ? {
|
|
256
|
+
eventNameConstant: {
|
|
257
|
+
sourceKind: 'package_static_string',
|
|
258
|
+
sourceFile: resolution.sourceFile,
|
|
259
|
+
sourceLine: resolution.sourceLine,
|
|
260
|
+
},
|
|
261
|
+
} : {}),
|
|
262
|
+
eventNamePackageConstantResolution: {
|
|
263
|
+
status: resolution.status,
|
|
264
|
+
reason: resolution.reason,
|
|
265
|
+
candidateCount: resolution.candidateCount,
|
|
266
|
+
eligibleCandidateCount: resolution.eligibleCandidateCount,
|
|
267
|
+
selectedCandidateCount: resolution.status === 'resolved' ? 1 : 0,
|
|
268
|
+
candidateSetComplete: resolution.complete,
|
|
269
|
+
requestedPackageName: call.binding.requestedPackageName,
|
|
270
|
+
requestedModuleSubpath: call.binding.requestedModuleSubpath,
|
|
271
|
+
requestedPublicName: call.binding.requestedPublicName,
|
|
272
|
+
resolvedModulePath: resolution.modulePath,
|
|
273
|
+
targetRepositoryId: resolution.targetRepoId,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function linkPackageEventConstants(
|
|
279
|
+
db: Db,
|
|
280
|
+
workspaceId: number,
|
|
281
|
+
): PackageEventConstantLinkSummary {
|
|
282
|
+
const summary = { resolved: 0, ambiguous: 0, unresolved: 0 };
|
|
283
|
+
const update = db.prepare(`UPDATE outbound_calls SET event_name_expr=?,
|
|
284
|
+
unresolved_reason=?,evidence_json=? WHERE id=?`);
|
|
285
|
+
for (const call of callRows(db, workspaceId)) {
|
|
286
|
+
const resolution = resolveCall(db, workspaceId, call);
|
|
287
|
+
const reason = receiverReason(call) ?? resolution.reason ?? null;
|
|
288
|
+
const sourceExpression = call.evidence.eventNameConstantSourceExpression;
|
|
289
|
+
update.run(
|
|
290
|
+
resolution.value ?? String(sourceExpression ?? ''),
|
|
291
|
+
reason,
|
|
292
|
+
resolutionEvidence(call, resolution),
|
|
293
|
+
call.id,
|
|
294
|
+
);
|
|
295
|
+
summary[resolution.status] += 1;
|
|
296
|
+
}
|
|
297
|
+
return summary;
|
|
298
|
+
}
|
|
@@ -23,7 +23,7 @@ function location(evidence: Record<string, unknown>): string {
|
|
|
23
23
|
export function renderTraceTable(result: TraceResult): string {
|
|
24
24
|
const lines = ['Step Type From To Evidence'];
|
|
25
25
|
for (const e of result.edges) {
|
|
26
|
-
lines.push(`${String(e.step).padEnd(5)} ${e.type.padEnd(20)} ${e.from.slice(0, 34).padEnd(35)} ${e.to.slice(0, 35).padEnd(36)} ${
|
|
26
|
+
lines.push(`${String(e.step).padEnd(5)} ${e.type.padEnd(20)} ${e.from.slice(0, 34).padEnd(35)} ${e.to.slice(0, 35).padEnd(36)} ${evidenceSummary(e.evidence)}`);
|
|
27
27
|
if (e.unresolvedReason)
|
|
28
28
|
lines.push(...hintLines(e.evidence).map((hint) => ` ${hint}`));
|
|
29
29
|
}
|
|
@@ -31,6 +31,18 @@ export function renderTraceTable(result: TraceResult): string {
|
|
|
31
31
|
return `${lines.join('\n')}\n`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function evidenceSummary(evidence: Record<string, unknown>): string {
|
|
35
|
+
const labels = [
|
|
36
|
+
typeof evidence.dispatchScope === 'string'
|
|
37
|
+
? `scope=${evidence.dispatchScope}` : undefined,
|
|
38
|
+
typeof evidence.dispatchCertainty === 'string'
|
|
39
|
+
? `certainty=${evidence.dispatchCertainty}` : undefined,
|
|
40
|
+
].filter((value): value is string => Boolean(value));
|
|
41
|
+
return labels.length > 0
|
|
42
|
+
? `${location(evidence)} [${labels.join(',')}]`
|
|
43
|
+
: location(evidence);
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
function diagnosticLines(diagnostic: Record<string, unknown>): string[] {
|
|
35
47
|
const first = `${String(diagnostic.severity ?? 'info')} ${String(diagnostic.code ?? 'diagnostic')} ${String(diagnostic.message ?? '')}`;
|
|
36
48
|
const details = diagnosticDetailLines(diagnostic);
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
type RepositorySourceContext,
|
|
15
15
|
} from './ts-project.js';
|
|
16
16
|
import { normalizePath } from '../utils/path-utils.js';
|
|
17
|
+
import {
|
|
18
|
+
collectStringConstantLookups,
|
|
19
|
+
type StringConstantLookups,
|
|
20
|
+
} from './string-constant-lookups.js';
|
|
17
21
|
|
|
18
22
|
type DecoratorResolution = HandlerMethodFact['decoratorResolution'];
|
|
19
23
|
type ResolvedArgumentKind = Exclude<
|
|
@@ -21,9 +25,7 @@ type ResolvedArgumentKind = Exclude<
|
|
|
21
25
|
'lifecycle_implicit' | 'unresolved'
|
|
22
26
|
>;
|
|
23
27
|
interface StringLookups {
|
|
24
|
-
|
|
25
|
-
enumMembers: Map<string, string>;
|
|
26
|
-
objectProperties: Map<string, string>;
|
|
28
|
+
strings: StringConstantLookups;
|
|
27
29
|
capDecoratorNames: Map<string, string>;
|
|
28
30
|
capDecoratorNamespaces: Set<string>;
|
|
29
31
|
}
|
|
@@ -94,60 +96,14 @@ function unwrapExpression(expression: ts.Expression): ts.Expression {
|
|
|
94
96
|
) current = current.expression;
|
|
95
97
|
return current;
|
|
96
98
|
}
|
|
97
|
-
function stringValue(expression: ts.Expression | undefined): string | undefined {
|
|
98
|
-
if (!expression) return undefined;
|
|
99
|
-
const unwrapped = unwrapExpression(expression);
|
|
100
|
-
return ts.isStringLiteralLike(unwrapped) ? unwrapped.text : undefined;
|
|
101
|
-
}
|
|
102
|
-
function propertyName(node: ts.PropertyName): string | undefined {
|
|
103
|
-
if (ts.isIdentifier(node) || ts.isStringLiteralLike(node)) return node.text;
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
function collectEnumMembers(
|
|
107
|
-
statement: ts.EnumDeclaration,
|
|
108
|
-
lookups: StringLookups,
|
|
109
|
-
): void {
|
|
110
|
-
for (const member of statement.members) {
|
|
111
|
-
const name = propertyName(member.name);
|
|
112
|
-
const value = stringValue(member.initializer);
|
|
113
|
-
if (name && value !== undefined)
|
|
114
|
-
lookups.enumMembers.set(`${statement.name.text}.${name}`, value);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function collectObjectProperties(
|
|
118
|
-
name: string,
|
|
119
|
-
initializer: ts.Expression,
|
|
120
|
-
lookups: StringLookups,
|
|
121
|
-
): void {
|
|
122
|
-
const object = unwrapExpression(initializer);
|
|
123
|
-
if (!ts.isObjectLiteralExpression(object)) return;
|
|
124
|
-
for (const property of object.properties) {
|
|
125
|
-
if (!ts.isPropertyAssignment(property)) continue;
|
|
126
|
-
const key = propertyName(property.name);
|
|
127
|
-
const value = stringValue(property.initializer);
|
|
128
|
-
if (key && value !== undefined)
|
|
129
|
-
lookups.objectProperties.set(`${name}.${key}`, value);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
99
|
function collectStringLookups(source: ts.SourceFile): StringLookups {
|
|
133
100
|
const lookups: StringLookups = {
|
|
134
|
-
|
|
135
|
-
enumMembers: new Map(),
|
|
136
|
-
objectProperties: new Map(),
|
|
101
|
+
strings: collectStringConstantLookups(source),
|
|
137
102
|
capDecoratorNames: new Map(),
|
|
138
103
|
capDecoratorNamespaces: new Set(),
|
|
139
104
|
};
|
|
140
105
|
for (const statement of source.statements) {
|
|
141
106
|
collectCapDecoratorImports(statement, lookups);
|
|
142
|
-
if (ts.isEnumDeclaration(statement)) collectEnumMembers(statement, lookups);
|
|
143
|
-
if (!ts.isVariableStatement(statement)
|
|
144
|
-
|| !(statement.declarationList.flags & ts.NodeFlags.Const)) continue;
|
|
145
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
146
|
-
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
147
|
-
const value = stringValue(declaration.initializer);
|
|
148
|
-
if (value !== undefined) lookups.identifiers.set(declaration.name.text, value);
|
|
149
|
-
collectObjectProperties(declaration.name.text, declaration.initializer, lookups);
|
|
150
|
-
}
|
|
151
107
|
}
|
|
152
108
|
return lookups;
|
|
153
109
|
}
|
|
@@ -226,7 +182,7 @@ function resolveDecoratorArgument(
|
|
|
226
182
|
if (ts.isStringLiteralLike(expression))
|
|
227
183
|
return resolved(rawExpression, argumentExpression, expression.text, 'literal');
|
|
228
184
|
if (ts.isIdentifier(expression)) {
|
|
229
|
-
const value = lookups.identifiers.get(expression.text);
|
|
185
|
+
const value = lookups.strings.identifiers.get(expression.text)?.value;
|
|
230
186
|
return value === undefined
|
|
231
187
|
? unresolved(rawExpression, 'identifier_not_resolved_to_local_const_string', argumentExpression)
|
|
232
188
|
: resolved(rawExpression, argumentExpression, value, 'const_identifier');
|
|
@@ -234,10 +190,10 @@ function resolveDecoratorArgument(
|
|
|
234
190
|
if (ts.isPropertyAccessExpression(expression)
|
|
235
191
|
&& ts.isIdentifier(expression.expression)) {
|
|
236
192
|
const key = `${expression.expression.text}.${expression.name.text}`;
|
|
237
|
-
const enumValue = lookups.enumMembers.get(key);
|
|
193
|
+
const enumValue = lookups.strings.enumMembers.get(key)?.value;
|
|
238
194
|
if (enumValue !== undefined)
|
|
239
195
|
return resolved(rawExpression, argumentExpression, enumValue, 'enum_member');
|
|
240
|
-
const objectValue = lookups.objectProperties.get(key);
|
|
196
|
+
const objectValue = lookups.strings.objectProperties.get(key)?.value;
|
|
241
197
|
if (objectValue !== undefined)
|
|
242
198
|
return resolved(rawExpression, argumentExpression, objectValue, 'const_object_property');
|
|
243
199
|
}
|