@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
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import {
|
|
4
|
+
hasNestedCommonJsMutation,
|
|
5
|
+
isUnshadowedCommonJsExportExpression,
|
|
6
|
+
unsupportedCommonJsMutation,
|
|
7
|
+
} from './018-package-commonjs-syntax.js';
|
|
8
|
+
import { stableLocalValueReference } from './020-stable-local-value.js';
|
|
9
|
+
|
|
10
|
+
export const PACKAGE_PUBLIC_SURFACE_SCHEMA =
|
|
11
|
+
'service-flow/package-public-surface@1';
|
|
12
|
+
export const PACKAGE_PUBLIC_SURFACE_RECORD_CAP = 256;
|
|
13
|
+
export const PACKAGE_PUBLIC_SURFACE_EXPORT_DEPTH = 16;
|
|
14
|
+
|
|
15
|
+
export type PackagePublicSurfaceStatus =
|
|
16
|
+
| 'complete'
|
|
17
|
+
| 'incomplete'
|
|
18
|
+
| 'unsupported'
|
|
19
|
+
| 'not_applicable';
|
|
20
|
+
export type ExecutableBodyReason =
|
|
21
|
+
| 'body_present'
|
|
22
|
+
| 'declaration_only'
|
|
23
|
+
| 'ambient_declaration'
|
|
24
|
+
| 'abstract_bodyless'
|
|
25
|
+
| 'overload_signature';
|
|
26
|
+
|
|
27
|
+
export interface PackageSourceModule {
|
|
28
|
+
sourceFile: string;
|
|
29
|
+
source: ts.SourceFile;
|
|
30
|
+
}
|
|
31
|
+
export interface ExecutableBodyEligibility {
|
|
32
|
+
eligible: boolean;
|
|
33
|
+
reason: ExecutableBodyReason;
|
|
34
|
+
}
|
|
35
|
+
export interface PublicSurfaceTarget {
|
|
36
|
+
sourceFile: string;
|
|
37
|
+
kind: 'function' | 'method' | 'object_method';
|
|
38
|
+
localName: string;
|
|
39
|
+
qualifiedName: string;
|
|
40
|
+
startOffset: number;
|
|
41
|
+
endOffset: number;
|
|
42
|
+
bodyEligibility: ExecutableBodyEligibility;
|
|
43
|
+
}
|
|
44
|
+
export interface PackagePublicEntry {
|
|
45
|
+
entry: string;
|
|
46
|
+
modulePath: string;
|
|
47
|
+
}
|
|
48
|
+
export interface PackagePublicScope {
|
|
49
|
+
entry: string;
|
|
50
|
+
modulePath: string;
|
|
51
|
+
publicName: string;
|
|
52
|
+
candidateCount: number;
|
|
53
|
+
eligibleCandidateCount: number;
|
|
54
|
+
selectedCandidateCount: 0 | 1;
|
|
55
|
+
candidateSetComplete: true;
|
|
56
|
+
targets: PublicSurfaceTarget[];
|
|
57
|
+
}
|
|
58
|
+
export interface PackagePublicSurfaceFact {
|
|
59
|
+
schema: typeof PACKAGE_PUBLIC_SURFACE_SCHEMA;
|
|
60
|
+
status: PackagePublicSurfaceStatus;
|
|
61
|
+
reason: string | null;
|
|
62
|
+
recordCap: typeof PACKAGE_PUBLIC_SURFACE_RECORD_CAP;
|
|
63
|
+
total: number;
|
|
64
|
+
shown: number;
|
|
65
|
+
omitted: number;
|
|
66
|
+
packageName: string | null;
|
|
67
|
+
exportsPresent: boolean;
|
|
68
|
+
exportsAuthoritative: boolean;
|
|
69
|
+
main: string | null;
|
|
70
|
+
module: string | null;
|
|
71
|
+
entries: PackagePublicEntry[];
|
|
72
|
+
scopes: PackagePublicScope[];
|
|
73
|
+
}
|
|
74
|
+
export interface SymbolPublicExposure {
|
|
75
|
+
entry: string;
|
|
76
|
+
modulePath: string;
|
|
77
|
+
publicName: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SymbolPublicSurfaceEvidence {
|
|
80
|
+
target: PublicSurfaceTarget;
|
|
81
|
+
exposures: SymbolPublicExposure[];
|
|
82
|
+
exposureTotal: number;
|
|
83
|
+
shownExposureCount: number;
|
|
84
|
+
omittedExposureCount: number;
|
|
85
|
+
}
|
|
86
|
+
export interface PackagePublicSurfaceAnalysis {
|
|
87
|
+
surface: PackagePublicSurfaceFact;
|
|
88
|
+
symbols: SymbolPublicSurfaceEvidence[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface LocalValue {
|
|
92
|
+
callable: PublicSurfaceTarget[];
|
|
93
|
+
members: Map<string, PublicSurfaceTarget[]>;
|
|
94
|
+
declarationOnlyCount: number;
|
|
95
|
+
}
|
|
96
|
+
interface LocalExport {
|
|
97
|
+
publicName: string;
|
|
98
|
+
localName: string;
|
|
99
|
+
typeOnly: boolean;
|
|
100
|
+
}
|
|
101
|
+
interface ReExport extends LocalExport {
|
|
102
|
+
importedName: string;
|
|
103
|
+
specifier: string;
|
|
104
|
+
}
|
|
105
|
+
export interface ModuleInfo {
|
|
106
|
+
modulePath: string;
|
|
107
|
+
sourceFile: string;
|
|
108
|
+
values: Map<string, LocalValue>;
|
|
109
|
+
localExports: LocalExport[];
|
|
110
|
+
reExports: ReExport[];
|
|
111
|
+
starExports: string[];
|
|
112
|
+
unstableValues: Set<string>;
|
|
113
|
+
unsupportedReason?: string;
|
|
114
|
+
}
|
|
115
|
+
export interface ResolvedExport {
|
|
116
|
+
targets: PublicSurfaceTarget[];
|
|
117
|
+
declarationOnlyCount: number;
|
|
118
|
+
}
|
|
119
|
+
export interface ModuleResolution {
|
|
120
|
+
exports: Map<string, ResolvedExport>;
|
|
121
|
+
complete: boolean;
|
|
122
|
+
reason?: string;
|
|
123
|
+
}
|
|
124
|
+
const extensionPattern = /\.(?:d\.)?(?:ts|js)$/;
|
|
125
|
+
|
|
126
|
+
function compareBinary(left: string, right: string): number {
|
|
127
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
|
|
131
|
+
return ts.canHaveModifiers(node)
|
|
132
|
+
&& Boolean(ts.getModifiers(node)?.some((item) => item.kind === kind));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function canonicalModulePath(sourceFile: string): string {
|
|
136
|
+
return sourceFile.replace(/\\/g, '/').replace(extensionPattern, '');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function targetKey(target: PublicSurfaceTarget): string {
|
|
140
|
+
return [
|
|
141
|
+
target.sourceFile, target.startOffset, target.endOffset,
|
|
142
|
+
target.kind, target.qualifiedName,
|
|
143
|
+
].join('\0');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function bodyEligibility(
|
|
147
|
+
node: ts.FunctionLikeDeclaration,
|
|
148
|
+
source: ts.SourceFile,
|
|
149
|
+
): ExecutableBodyEligibility {
|
|
150
|
+
if (node.body) return { eligible: true, reason: 'body_present' };
|
|
151
|
+
if (hasModifier(node, ts.SyntaxKind.AbstractKeyword))
|
|
152
|
+
return { eligible: false, reason: 'abstract_bodyless' };
|
|
153
|
+
if (hasModifier(node, ts.SyntaxKind.DeclareKeyword))
|
|
154
|
+
return { eligible: false, reason: 'ambient_declaration' };
|
|
155
|
+
return {
|
|
156
|
+
eligible: false,
|
|
157
|
+
reason: source.isDeclarationFile
|
|
158
|
+
? 'declaration_only'
|
|
159
|
+
: 'overload_signature',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function functionTarget(
|
|
164
|
+
info: ModuleInfo,
|
|
165
|
+
source: ts.SourceFile,
|
|
166
|
+
node: ts.FunctionLikeDeclaration,
|
|
167
|
+
localName: string,
|
|
168
|
+
kind: PublicSurfaceTarget['kind'] = 'function',
|
|
169
|
+
qualifiedName = localName,
|
|
170
|
+
): PublicSurfaceTarget {
|
|
171
|
+
return {
|
|
172
|
+
sourceFile: info.sourceFile, kind, localName, qualifiedName,
|
|
173
|
+
startOffset: node.getStart(source), endOffset: node.getEnd(),
|
|
174
|
+
bodyEligibility: bodyEligibility(node, source),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function emptyLocalValue(declarationOnlyCount = 0): LocalValue {
|
|
179
|
+
return { callable: [], members: new Map(), declarationOnlyCount };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function localValue(values: Map<string, LocalValue>, name: string): LocalValue {
|
|
183
|
+
const value = values.get(name) ?? emptyLocalValue();
|
|
184
|
+
values.set(name, value);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function addMember(
|
|
189
|
+
value: LocalValue,
|
|
190
|
+
name: string,
|
|
191
|
+
target: PublicSurfaceTarget,
|
|
192
|
+
): void {
|
|
193
|
+
const targets = value.members.get(name) ?? [];
|
|
194
|
+
targets.push(target);
|
|
195
|
+
value.members.set(name, targets);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function propertyName(node: ts.PropertyName | undefined): string | undefined {
|
|
199
|
+
if (!node) return undefined;
|
|
200
|
+
if (ts.isIdentifier(node) || ts.isStringLiteralLike(node)
|
|
201
|
+
|| ts.isNumericLiteral(node)) return node.text;
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isPublicStaticMember(member: ts.ClassElement): boolean {
|
|
206
|
+
if (!hasModifier(member, ts.SyntaxKind.StaticKeyword)) return false;
|
|
207
|
+
if (hasModifier(member, ts.SyntaxKind.PrivateKeyword)
|
|
208
|
+
|| hasModifier(member, ts.SyntaxKind.ProtectedKeyword)) return false;
|
|
209
|
+
return !member.name || !ts.isPrivateIdentifier(member.name);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function collectFunction(
|
|
213
|
+
info: ModuleInfo,
|
|
214
|
+
source: ts.SourceFile,
|
|
215
|
+
node: ts.FunctionDeclaration,
|
|
216
|
+
): void {
|
|
217
|
+
if (!node.name) {
|
|
218
|
+
if (hasModifier(node, ts.SyntaxKind.ExportKeyword))
|
|
219
|
+
info.unsupportedReason = 'anonymous_default_export_without_symbol_identity';
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
localValue(info.values, node.name.text).callable.push(
|
|
223
|
+
functionTarget(info, source, node, node.name.text),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function collectClassMember(
|
|
228
|
+
info: ModuleInfo,
|
|
229
|
+
source: ts.SourceFile,
|
|
230
|
+
className: string,
|
|
231
|
+
value: LocalValue,
|
|
232
|
+
member: ts.ClassElement,
|
|
233
|
+
): void {
|
|
234
|
+
if (!isPublicStaticMember(member)) return;
|
|
235
|
+
const name = propertyName(member.name);
|
|
236
|
+
if (!name) return;
|
|
237
|
+
if (ts.isMethodDeclaration(member)) {
|
|
238
|
+
addMember(value, name, functionTarget(
|
|
239
|
+
info, source, member, name, 'method', `${className}.${name}`,
|
|
240
|
+
));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (!ts.isPropertyDeclaration(member) || !member.initializer
|
|
244
|
+
|| (!ts.isArrowFunction(member.initializer)
|
|
245
|
+
&& !ts.isFunctionExpression(member.initializer))) return;
|
|
246
|
+
addMember(value, name, functionTarget(
|
|
247
|
+
info, source, member.initializer, name, 'method', `${className}.${name}`,
|
|
248
|
+
));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function collectClassMembers(
|
|
252
|
+
info: ModuleInfo,
|
|
253
|
+
source: ts.SourceFile,
|
|
254
|
+
node: ts.ClassDeclaration,
|
|
255
|
+
): void {
|
|
256
|
+
if (!node.name) return;
|
|
257
|
+
if (!stableLocalValueReference(source, node.name)) {
|
|
258
|
+
info.unstableValues.add(node.name.text);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const value = localValue(info.values, node.name.text);
|
|
262
|
+
for (const member of node.members)
|
|
263
|
+
collectClassMember(info, source, node.name.text, value, member);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function collectObjectMembers(
|
|
267
|
+
info: ModuleInfo,
|
|
268
|
+
source: ts.SourceFile,
|
|
269
|
+
objectName: string,
|
|
270
|
+
object: ts.ObjectLiteralExpression,
|
|
271
|
+
): void {
|
|
272
|
+
const value = localValue(info.values, objectName);
|
|
273
|
+
for (const member of object.properties) {
|
|
274
|
+
const name = propertyName(member.name);
|
|
275
|
+
if (!name) continue;
|
|
276
|
+
const callable = ts.isMethodDeclaration(member)
|
|
277
|
+
? member
|
|
278
|
+
: ts.isPropertyAssignment(member)
|
|
279
|
+
&& (ts.isArrowFunction(member.initializer)
|
|
280
|
+
|| ts.isFunctionExpression(member.initializer))
|
|
281
|
+
? member.initializer
|
|
282
|
+
: undefined;
|
|
283
|
+
if (callable) addMember(value, name, functionTarget(
|
|
284
|
+
info, source, callable, `${objectName}.${name}`,
|
|
285
|
+
'object_method', `${objectName}.${name}`,
|
|
286
|
+
));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function collectVariables(
|
|
291
|
+
info: ModuleInfo,
|
|
292
|
+
source: ts.SourceFile,
|
|
293
|
+
statement: ts.VariableStatement,
|
|
294
|
+
): void {
|
|
295
|
+
const immutable = (statement.declarationList.flags & ts.NodeFlags.Const) !== 0;
|
|
296
|
+
for (const item of statement.declarationList.declarations) {
|
|
297
|
+
if (!ts.isIdentifier(item.name)) continue;
|
|
298
|
+
const name = item.name.text;
|
|
299
|
+
if (!immutable || !stableLocalValueReference(source, item.name)) {
|
|
300
|
+
info.unstableValues.add(name);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!item.initializer) continue;
|
|
304
|
+
if (ts.isArrowFunction(item.initializer)
|
|
305
|
+
|| ts.isFunctionExpression(item.initializer))
|
|
306
|
+
localValue(info.values, name).callable.push(
|
|
307
|
+
functionTarget(info, source, item.initializer, name),
|
|
308
|
+
);
|
|
309
|
+
if (ts.isObjectLiteralExpression(item.initializer))
|
|
310
|
+
collectObjectMembers(info, source, name, item.initializer);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function pushLocalExport(
|
|
315
|
+
info: ModuleInfo,
|
|
316
|
+
publicName: string,
|
|
317
|
+
localName: string,
|
|
318
|
+
typeOnly = false,
|
|
319
|
+
): void {
|
|
320
|
+
const rootName = localName.split('.')[0] ?? localName;
|
|
321
|
+
if (info.unstableValues.has(rootName))
|
|
322
|
+
info.unsupportedReason = 'unsupported_mutable_export_binding';
|
|
323
|
+
info.localExports.push({ publicName, localName, typeOnly });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function addDirectExports(info: ModuleInfo, statement: ts.Statement): void {
|
|
327
|
+
if (!hasModifier(statement, ts.SyntaxKind.ExportKeyword)) return;
|
|
328
|
+
const exportedName = (name: string): string =>
|
|
329
|
+
hasModifier(statement, ts.SyntaxKind.DefaultKeyword) ? 'default' : name;
|
|
330
|
+
if (ts.isFunctionDeclaration(statement) && statement.name)
|
|
331
|
+
pushLocalExport(info, exportedName(statement.name.text), statement.name.text);
|
|
332
|
+
if (ts.isClassDeclaration(statement) && statement.name)
|
|
333
|
+
pushLocalExport(info, exportedName(statement.name.text), statement.name.text);
|
|
334
|
+
if (ts.isVariableStatement(statement))
|
|
335
|
+
for (const item of statement.declarationList.declarations)
|
|
336
|
+
if (ts.isIdentifier(item.name))
|
|
337
|
+
pushLocalExport(info, item.name.text, item.name.text);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function addNamedExports(
|
|
341
|
+
info: ModuleInfo,
|
|
342
|
+
statement: ts.ExportDeclaration,
|
|
343
|
+
): void {
|
|
344
|
+
const clause = statement.exportClause;
|
|
345
|
+
if (!clause || !ts.isNamedExports(clause)) return;
|
|
346
|
+
const module = statement.moduleSpecifier;
|
|
347
|
+
const specifier = module && ts.isStringLiteralLike(module)
|
|
348
|
+
? module.text
|
|
349
|
+
: undefined;
|
|
350
|
+
for (const item of clause.elements) {
|
|
351
|
+
const importedName = item.propertyName?.text ?? item.name.text;
|
|
352
|
+
const typeOnly = statement.isTypeOnly || item.isTypeOnly;
|
|
353
|
+
if (specifier) info.reExports.push({
|
|
354
|
+
publicName: item.name.text, localName: importedName,
|
|
355
|
+
importedName, specifier, typeOnly,
|
|
356
|
+
});
|
|
357
|
+
else pushLocalExport(info, item.name.text, importedName, typeOnly);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function addExportDeclaration(
|
|
362
|
+
info: ModuleInfo,
|
|
363
|
+
statement: ts.ExportDeclaration,
|
|
364
|
+
): void {
|
|
365
|
+
if (statement.exportClause) {
|
|
366
|
+
if (ts.isNamedExports(statement.exportClause))
|
|
367
|
+
addNamedExports(info, statement);
|
|
368
|
+
else info.unsupportedReason = 'unsupported_namespace_reexport';
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const module = statement.moduleSpecifier;
|
|
372
|
+
if (!module || !ts.isStringLiteralLike(module)) {
|
|
373
|
+
info.unsupportedReason = 'unsupported_export_declaration';
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
info.starExports.push(module.text);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function addExportAssignment(
|
|
380
|
+
info: ModuleInfo,
|
|
381
|
+
statement: ts.ExportAssignment,
|
|
382
|
+
): void {
|
|
383
|
+
if (!statement.isExportEquals && ts.isIdentifier(statement.expression))
|
|
384
|
+
pushLocalExport(info, 'default', statement.expression.text);
|
|
385
|
+
else info.unsupportedReason = 'unsupported_export_assignment';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function addCjsObjectExports(
|
|
389
|
+
info: ModuleInfo,
|
|
390
|
+
object: ts.ObjectLiteralExpression,
|
|
391
|
+
): void {
|
|
392
|
+
for (const item of object.properties) {
|
|
393
|
+
if (ts.isShorthandPropertyAssignment(item))
|
|
394
|
+
pushLocalExport(info, item.name.text, item.name.text);
|
|
395
|
+
else if (ts.isPropertyAssignment(item)
|
|
396
|
+
&& ts.isIdentifier(item.initializer)) {
|
|
397
|
+
const name = propertyName(item.name);
|
|
398
|
+
if (name) pushLocalExport(info, name, item.initializer.text);
|
|
399
|
+
} else info.unsupportedReason = 'unsupported_commonjs_export_shape';
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function addCjsNamespaceExport(info: ModuleInfo, localName: string): void {
|
|
404
|
+
const value = info.values.get(localName);
|
|
405
|
+
if (!value) {
|
|
406
|
+
info.unsupportedReason = 'unsupported_commonjs_export_shape';
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (value.callable.length > 0) pushLocalExport(info, 'default', localName);
|
|
410
|
+
for (const member of value.members.keys()) {
|
|
411
|
+
pushLocalExport(info, member, `${localName}.${member}`);
|
|
412
|
+
pushLocalExport(info, `default.${member}`, `${localName}.${member}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function cjsBinaryAssignment(
|
|
417
|
+
statement: ts.ExpressionStatement,
|
|
418
|
+
): ts.BinaryExpression | undefined {
|
|
419
|
+
const value = statement.expression;
|
|
420
|
+
if (!ts.isBinaryExpression(value)) return undefined;
|
|
421
|
+
return value.operatorToken.kind === ts.SyntaxKind.EqualsToken
|
|
422
|
+
? value
|
|
423
|
+
: undefined;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function cjsAssignment(
|
|
427
|
+
info: ModuleInfo,
|
|
428
|
+
statement: ts.ExpressionStatement,
|
|
429
|
+
): void {
|
|
430
|
+
const value = cjsBinaryAssignment(statement);
|
|
431
|
+
if (!value) return;
|
|
432
|
+
const left = value.left.getText();
|
|
433
|
+
if (!isUnshadowedCommonJsExportExpression(value.left)) return;
|
|
434
|
+
if (left === 'module.exports' && ts.isObjectLiteralExpression(value.right))
|
|
435
|
+
return addCjsObjectExports(info, value.right);
|
|
436
|
+
if (left === 'module.exports' && ts.isIdentifier(value.right))
|
|
437
|
+
return addCjsNamespaceExport(info, value.right.text);
|
|
438
|
+
const match = /^(?:exports|module\.exports)\.([A-Za-z_$][\w$]*)$/.exec(left);
|
|
439
|
+
if (match?.[1] && ts.isIdentifier(value.right))
|
|
440
|
+
return pushLocalExport(info, match[1], value.right.text);
|
|
441
|
+
info.unsupportedReason = 'unsupported_commonjs_export_shape';
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function collectTypeDeclaration(
|
|
445
|
+
info: ModuleInfo,
|
|
446
|
+
statement: ts.Statement,
|
|
447
|
+
): void {
|
|
448
|
+
if (!(ts.isInterfaceDeclaration(statement)
|
|
449
|
+
|| ts.isTypeAliasDeclaration(statement))
|
|
450
|
+
|| !hasModifier(statement, ts.SyntaxKind.ExportKeyword)) return;
|
|
451
|
+
localValue(info.values, statement.name.text).declarationOnlyCount += 1;
|
|
452
|
+
pushLocalExport(info, statement.name.text, statement.name.text, true);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function moduleInfo(module: PackageSourceModule): ModuleInfo {
|
|
456
|
+
const info: ModuleInfo = {
|
|
457
|
+
modulePath: canonicalModulePath(module.sourceFile),
|
|
458
|
+
sourceFile: module.sourceFile.replace(/\\/g, '/'),
|
|
459
|
+
values: new Map(), localExports: [], reExports: [], starExports: [],
|
|
460
|
+
unstableValues: new Set(),
|
|
461
|
+
};
|
|
462
|
+
for (const statement of module.source.statements) {
|
|
463
|
+
if (ts.isFunctionDeclaration(statement))
|
|
464
|
+
collectFunction(info, module.source, statement);
|
|
465
|
+
if (ts.isClassDeclaration(statement))
|
|
466
|
+
collectClassMembers(info, module.source, statement);
|
|
467
|
+
if (ts.isVariableStatement(statement))
|
|
468
|
+
collectVariables(info, module.source, statement);
|
|
469
|
+
addDirectExports(info, statement);
|
|
470
|
+
if (ts.isExportDeclaration(statement))
|
|
471
|
+
addExportDeclaration(info, statement);
|
|
472
|
+
if (ts.isExportAssignment(statement))
|
|
473
|
+
addExportAssignment(info, statement);
|
|
474
|
+
if (ts.isExpressionStatement(statement)) {
|
|
475
|
+
cjsAssignment(info, statement);
|
|
476
|
+
if (unsupportedCommonJsMutation(statement))
|
|
477
|
+
info.unsupportedReason = 'unsupported_commonjs_export_shape';
|
|
478
|
+
}
|
|
479
|
+
collectTypeDeclaration(info, statement);
|
|
480
|
+
}
|
|
481
|
+
if (hasNestedCommonJsMutation(module.source))
|
|
482
|
+
info.unsupportedReason = 'unsupported_commonjs_export_shape';
|
|
483
|
+
return info;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function mergeTargets(
|
|
487
|
+
current: readonly PublicSurfaceTarget[],
|
|
488
|
+
incoming: readonly PublicSurfaceTarget[],
|
|
489
|
+
): PublicSurfaceTarget[] {
|
|
490
|
+
const byKey = new Map(current.map((item) => [targetKey(item), item]));
|
|
491
|
+
for (const item of incoming) byKey.set(targetKey(item), item);
|
|
492
|
+
return [...byKey.values()].sort((left, right) =>
|
|
493
|
+
compareBinary(targetKey(left), targetKey(right)));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function mergeExport(
|
|
497
|
+
exports: Map<string, ResolvedExport>,
|
|
498
|
+
publicName: string,
|
|
499
|
+
incoming: ResolvedExport,
|
|
500
|
+
): void {
|
|
501
|
+
const current = exports.get(publicName)
|
|
502
|
+
?? { targets: [], declarationOnlyCount: 0 };
|
|
503
|
+
exports.set(publicName, {
|
|
504
|
+
targets: mergeTargets(current.targets, incoming.targets),
|
|
505
|
+
declarationOnlyCount:
|
|
506
|
+
current.declarationOnlyCount + incoming.declarationOnlyCount,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function localExportValue(
|
|
511
|
+
info: ModuleInfo,
|
|
512
|
+
localName: string,
|
|
513
|
+
): LocalValue | undefined {
|
|
514
|
+
const direct = info.values.get(localName);
|
|
515
|
+
if (direct) return direct;
|
|
516
|
+
const separator = localName.lastIndexOf('.');
|
|
517
|
+
if (separator < 1) return undefined;
|
|
518
|
+
const owner = info.values.get(localName.slice(0, separator));
|
|
519
|
+
const targets = owner?.members.get(localName.slice(separator + 1));
|
|
520
|
+
return targets
|
|
521
|
+
? { callable: targets, members: new Map(), declarationOnlyCount: 0 }
|
|
522
|
+
: undefined;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function applyLocalExport(
|
|
526
|
+
exports: Map<string, ResolvedExport>,
|
|
527
|
+
info: ModuleInfo,
|
|
528
|
+
item: LocalExport,
|
|
529
|
+
): void {
|
|
530
|
+
const value = localExportValue(info, item.localName);
|
|
531
|
+
if (!value || item.typeOnly) {
|
|
532
|
+
mergeExport(exports, item.publicName, {
|
|
533
|
+
targets: [], declarationOnlyCount: value?.declarationOnlyCount ?? 1,
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (value.callable.length > 0 || value.declarationOnlyCount > 0)
|
|
538
|
+
mergeExport(exports, item.publicName, {
|
|
539
|
+
targets: value.callable,
|
|
540
|
+
declarationOnlyCount: value.declarationOnlyCount,
|
|
541
|
+
});
|
|
542
|
+
for (const [member, targets] of value.members)
|
|
543
|
+
mergeExport(exports, `${item.publicName}.${member}`, {
|
|
544
|
+
targets, declarationOnlyCount: 0,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function requestedModuleCandidates(
|
|
549
|
+
fromModule: string,
|
|
550
|
+
specifier: string,
|
|
551
|
+
infos: Map<string, ModuleInfo[]>,
|
|
552
|
+
): string[] {
|
|
553
|
+
if (!specifier.startsWith('.')) return [];
|
|
554
|
+
const joined = posix.normalize(posix.join(posix.dirname(fromModule), specifier));
|
|
555
|
+
if (joined === '..' || joined.startsWith('../')) return [];
|
|
556
|
+
const base = canonicalModulePath(joined.replace(/^\.\//, ''));
|
|
557
|
+
return [base, `${base}/index`].filter((item, index, values) =>
|
|
558
|
+
values.indexOf(item) === index && infos.get(item)?.length === 1);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function renameExport(
|
|
562
|
+
exports: Map<string, ResolvedExport>,
|
|
563
|
+
source: Map<string, ResolvedExport>,
|
|
564
|
+
item: ReExport,
|
|
565
|
+
): void {
|
|
566
|
+
const exact = source.get(item.importedName);
|
|
567
|
+
const typeOnly = (value: ResolvedExport): ResolvedExport => item.typeOnly
|
|
568
|
+
? { targets: [], declarationOnlyCount: Math.max(
|
|
569
|
+
1, value.declarationOnlyCount,
|
|
570
|
+
) }
|
|
571
|
+
: value;
|
|
572
|
+
if (exact) mergeExport(exports, item.publicName, typeOnly(exact));
|
|
573
|
+
for (const [name, value] of source) {
|
|
574
|
+
if (!name.startsWith(`${item.importedName}.`)) continue;
|
|
575
|
+
mergeExport(exports, `${item.publicName}${name.slice(
|
|
576
|
+
item.importedName.length,
|
|
577
|
+
)}`, typeOnly(value));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function moduleFailure(reason: string): ModuleResolution {
|
|
582
|
+
return { exports: new Map(), complete: false, reason };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function nestedResolution(
|
|
586
|
+
info: ModuleInfo,
|
|
587
|
+
specifier: string,
|
|
588
|
+
infos: Map<string, ModuleInfo[]>,
|
|
589
|
+
memo: Map<string, ModuleResolution>,
|
|
590
|
+
stack: readonly string[],
|
|
591
|
+
): ModuleResolution {
|
|
592
|
+
if (!specifier.startsWith('.'))
|
|
593
|
+
return moduleFailure('unsupported_external_reexport');
|
|
594
|
+
const candidates = requestedModuleCandidates(info.modulePath, specifier, infos);
|
|
595
|
+
if (candidates.length !== 1 || !candidates[0])
|
|
596
|
+
return moduleFailure(candidates.length > 1
|
|
597
|
+
? 'public_surface_module_resolution_ambiguous'
|
|
598
|
+
: 'public_surface_module_not_indexed');
|
|
599
|
+
return resolveModule(candidates[0], infos, memo, stack);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function resolveModuleInfo(
|
|
603
|
+
info: ModuleInfo,
|
|
604
|
+
infos: Map<string, ModuleInfo[]>,
|
|
605
|
+
memo: Map<string, ModuleResolution>,
|
|
606
|
+
stack: readonly string[],
|
|
607
|
+
): ModuleResolution {
|
|
608
|
+
const exports = new Map<string, ResolvedExport>();
|
|
609
|
+
for (const item of info.localExports) applyLocalExport(exports, info, item);
|
|
610
|
+
if (info.unsupportedReason)
|
|
611
|
+
return { exports, complete: false, reason: info.unsupportedReason };
|
|
612
|
+
for (const item of info.reExports) {
|
|
613
|
+
const nested = nestedResolution(info, item.specifier, infos, memo, stack);
|
|
614
|
+
if (!nested.complete) return nested;
|
|
615
|
+
renameExport(exports, nested.exports, item);
|
|
616
|
+
}
|
|
617
|
+
for (const specifier of info.starExports) {
|
|
618
|
+
const nested = nestedResolution(info, specifier, infos, memo, stack);
|
|
619
|
+
if (!nested.complete) return nested;
|
|
620
|
+
for (const [name, value] of nested.exports)
|
|
621
|
+
if (name !== 'default' && !name.startsWith('default.'))
|
|
622
|
+
mergeExport(exports, name, value);
|
|
623
|
+
}
|
|
624
|
+
return { exports, complete: true };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export function resolveModule(
|
|
628
|
+
modulePath: string,
|
|
629
|
+
infos: Map<string, ModuleInfo[]>,
|
|
630
|
+
memo: Map<string, ModuleResolution>,
|
|
631
|
+
stack: readonly string[] = [],
|
|
632
|
+
): ModuleResolution {
|
|
633
|
+
const cached = memo.get(modulePath);
|
|
634
|
+
if (cached) return cached;
|
|
635
|
+
if (stack.includes(modulePath))
|
|
636
|
+
return moduleFailure('public_surface_reexport_cycle');
|
|
637
|
+
if (stack.length >= PACKAGE_PUBLIC_SURFACE_EXPORT_DEPTH)
|
|
638
|
+
return moduleFailure('public_surface_reexport_depth_exceeded');
|
|
639
|
+
const rows = infos.get(modulePath) ?? [];
|
|
640
|
+
if (rows.length !== 1 || !rows[0])
|
|
641
|
+
return moduleFailure(rows.length > 1
|
|
642
|
+
? 'public_surface_module_resolution_ambiguous'
|
|
643
|
+
: 'public_surface_module_not_indexed');
|
|
644
|
+
const result = resolveModuleInfo(rows[0], infos, memo, [...stack, modulePath]);
|
|
645
|
+
if (result.complete) memo.set(modulePath, result);
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export function moduleInfoMap(
|
|
650
|
+
modules: readonly PackageSourceModule[],
|
|
651
|
+
): Map<string, ModuleInfo[]> {
|
|
652
|
+
const result = new Map<string, ModuleInfo[]>();
|
|
653
|
+
for (const module of modules) {
|
|
654
|
+
const info = moduleInfo(module);
|
|
655
|
+
result.set(info.modulePath, [...(result.get(info.modulePath) ?? []), info]);
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
export { analyzePackagePublicSurface } from
|
|
661
|
+
'./010-package-public-surface-analysis.js';
|