@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.105
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/README.md +14 -0
- package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
- package/dist/declarations/bidirectional-target-change.d.ts +10 -0
- package/dist/declarations/js-ts-safe-member-merge.d.ts +86 -0
- package/dist/declarations/js-ts-safe-merge.d.ts +131 -0
- package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
- package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
- package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
- package/dist/declarations/native-import-losses.d.ts +3 -0
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
- package/dist/declarations/semantic-edit-script.d.ts +7 -4
- package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
- package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
- package/dist/declarations/semantic-transform-identity.d.ts +3 -0
- package/dist/declarations/source-preservation.d.ts +72 -0
- package/dist/declarations/universal-capability.d.ts +4 -0
- package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
- package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
- package/dist/declarations/universal-conversion-plan.d.ts +6 -1
- package/dist/declarations/universal-representation-coverage.d.ts +90 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
- package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
- package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
- package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
- package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
- package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
- package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
- package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
- package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
- package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
- package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
- package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
- package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
- package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
- package/dist/internal/index-impl/semanticEditScripts.js +4 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
- package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
- package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
- package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
- package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
- package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
- package/dist/js-ts-safe-member-merge-result.js +158 -0
- package/dist/js-ts-safe-member-merge.js +265 -0
- package/dist/js-ts-safe-merge-analyze.js +279 -0
- package/dist/js-ts-safe-merge-composed.js +170 -0
- package/dist/js-ts-safe-merge-constants.js +50 -0
- package/dist/js-ts-safe-merge-context.js +118 -0
- package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
- package/dist/js-ts-safe-merge-ledger.js +85 -0
- package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
- package/dist/js-ts-safe-merge-parse-statements.js +155 -0
- package/dist/js-ts-safe-merge-plan.js +190 -0
- package/dist/js-ts-safe-merge.js +175 -0
- package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
- package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
- package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
- package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
- package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
- package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
- package/dist/js-ts-semantic-merge-contracts.js +217 -0
- package/dist/js-ts-semantic-merge-member-containers.js +100 -0
- package/dist/js-ts-semantic-merge-member-keys.js +142 -0
- package/dist/js-ts-semantic-merge-member-segments.js +185 -0
- package/dist/js-ts-semantic-merge-member-source.js +82 -0
- package/dist/js-ts-semantic-merge-member-utils.js +18 -0
- package/dist/js-ts-semantic-merge-parse.js +16 -0
- package/dist/js-ts-semantic-merge.js +24 -0
- package/dist/lightweight-dependency-effects.js +51 -0
- package/dist/lightweight-dependency-language.js +12 -1
- package/dist/lightweight-dependency-relations.js +14 -27
- package/dist/native-region-scanner-core.js +33 -1
- package/dist/native-region-scanner-csharp.js +151 -0
- package/dist/native-region-scanner-dart.js +91 -0
- package/dist/native-region-scanner-dynamic.js +21 -151
- package/dist/native-region-scanner-functional.js +40 -13
- package/dist/native-region-scanner-java.js +97 -0
- package/dist/native-region-scanner-js-class.js +100 -0
- package/dist/native-region-scanner-js-helpers.js +28 -86
- package/dist/native-region-scanner-js-imports.js +121 -1
- package/dist/native-region-scanner-js-nested.js +96 -8
- package/dist/native-region-scanner-js-structure.js +27 -0
- package/dist/native-region-scanner-js-types.js +99 -0
- package/dist/native-region-scanner-js.js +70 -118
- package/dist/native-region-scanner-kotlin.js +94 -0
- package/dist/native-region-scanner-main.js +15 -181
- package/dist/native-region-scanner-php.js +80 -0
- package/dist/native-region-scanner-python.js +62 -0
- package/dist/native-region-scanner-ruby.js +72 -0
- package/dist/native-region-scanner-scala.js +91 -0
- package/dist/native-region-scanner-spans.js +74 -0
- package/dist/native-region-scanner-swift.js +155 -0
- package/dist/native-region-scanner.js +14 -10
- package/dist/native-source-ledger-helpers.js +195 -0
- package/dist/native-source-ledger.js +306 -0
- package/dist/native-source-preservation-scanner.js +4 -0
- package/dist/semantic-import-callsite-regions.js +136 -0
- package/dist/semantic-import-effect-regions.js +283 -0
- package/dist/semantic-import-regions.js +11 -2
- package/dist/semantic-import-sidecar-entry.js +16 -2
- package/dist/semantic-import-sidecar-types.d.ts +2 -0
- package/dist/semantic-sidecar-example.js +68 -0
- package/dist/universal-capability-matrix.js +23 -0
- package/dist/universal-conversion-artifact-query.js +79 -2
- package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
- package/dist/universal-conversion-artifact-summary.js +33 -1
- package/dist/universal-conversion-artifacts.js +13 -48
- package/dist/universal-conversion-plan-scoring.js +21 -1
- package/dist/universal-conversion-plan-summary.js +30 -0
- package/dist/universal-conversion-plan.js +25 -9
- package/dist/universal-conversion-route-metadata.js +96 -0
- package/dist/universal-conversion-route-operations.js +7 -0
- package/dist/universal-representation-coverage.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyMemberAdditions,
|
|
3
|
+
applyPreparedMemberAdditions,
|
|
4
|
+
canonicalizeSourceBodies,
|
|
5
|
+
findContainer,
|
|
6
|
+
normalizeKind,
|
|
7
|
+
normalizeMemberText,
|
|
8
|
+
parseMembers,
|
|
9
|
+
uniqueStrings
|
|
10
|
+
} from './js-ts-semantic-merge-parse.js';
|
|
11
|
+
import { mergeResult } from './js-ts-safe-member-merge-result.js';
|
|
12
|
+
|
|
13
|
+
const NonSemanticRegionKinds = new Set(['property', 'type', 'config', 'content']);
|
|
14
|
+
const OrderSensitiveRegionKinds = new Set(['body', 'call', 'controlFlow', 'effect', 'import', 'mutation', 'route']);
|
|
15
|
+
|
|
16
|
+
function safeMergeJsTsMembers(input = {}) {
|
|
17
|
+
return mergeJsTsSafeMemberAdditions(input);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function mergeJsTsSafeMemberAdditions(input = {}) {
|
|
21
|
+
const analysis = analyzeJsTsSafeMemberAdditions(input);
|
|
22
|
+
const uniqueReasons = uniqueStrings(analysis.reasonCodes);
|
|
23
|
+
if (uniqueReasons.length) {
|
|
24
|
+
return mergeResult('rejected', undefined, uniqueReasons, analysis.preparedRegions, input, analysis.explicitPolicy);
|
|
25
|
+
}
|
|
26
|
+
const sourceText = applyMemberAdditions(analysis.headSourceText, analysis.preparedRegions);
|
|
27
|
+
return mergeResult('merged', sourceText, [], analysis.preparedRegions, input, analysis.explicitPolicy);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function analyzeJsTsSafeMemberAdditions(input = {}) {
|
|
31
|
+
const analysis = prepareJsTsSafeMemberAdditions(input);
|
|
32
|
+
const reasonCodes = [...analysis.reasonCodes];
|
|
33
|
+
if (!input.allowNonPolicySourceChanges) {
|
|
34
|
+
reasonCodes.push(...nonPolicySourceChangeReasons(analysis));
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
...analysis,
|
|
38
|
+
reasonCodes: uniqueStrings(reasonCodes),
|
|
39
|
+
ok: reasonCodes.length === 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function neutralizeJsTsSafeMemberMergeSources(input = {}) {
|
|
44
|
+
const analysis = analyzeJsTsSafeMemberAdditions({
|
|
45
|
+
...input,
|
|
46
|
+
allowNonPolicySourceChanges: true
|
|
47
|
+
});
|
|
48
|
+
if (analysis.reasonCodes.length) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
analysis,
|
|
52
|
+
result: mergeResult('rejected', undefined, analysis.reasonCodes, analysis.preparedRegions, input, analysis.explicitPolicy)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
analysis,
|
|
58
|
+
baseSourceText: analysis.baseSourceText,
|
|
59
|
+
workerSourceText: canonicalizeSourceBodies(analysis.workerSourceText, analysis.preparedRegions, 'worker'),
|
|
60
|
+
headSourceText: canonicalizeSourceBodies(analysis.headSourceText, analysis.preparedRegions, 'head')
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function applyJsTsPreparedMemberAdditions(sourceText, preparedRegions, sides) {
|
|
65
|
+
return applyPreparedMemberAdditions(sourceText, preparedRegions, sides);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function prepareJsTsSafeMemberAdditions(input = {}) {
|
|
69
|
+
const baseSourceText = input.baseSourceText;
|
|
70
|
+
const workerSourceText = input.workerSourceText;
|
|
71
|
+
const headSourceText = input.headSourceText;
|
|
72
|
+
const reasonCodes = [];
|
|
73
|
+
if (typeof baseSourceText !== 'string') reasonCodes.push('missing-base-source-text');
|
|
74
|
+
if (typeof workerSourceText !== 'string') reasonCodes.push('missing-worker-source-text');
|
|
75
|
+
if (typeof headSourceText !== 'string') reasonCodes.push('missing-head-source-text');
|
|
76
|
+
const policyRegions = normalizePolicyRegions(input.policy ?? input.mergePolicy ?? input);
|
|
77
|
+
if (!policyRegions.length) reasonCodes.push('missing-unordered-region-policy');
|
|
78
|
+
const preparedRegions = [];
|
|
79
|
+
for (const region of policyRegions) {
|
|
80
|
+
const policyReasons = validatePolicyRegion(region);
|
|
81
|
+
reasonCodes.push(...policyReasons);
|
|
82
|
+
if (policyReasons.length) continue;
|
|
83
|
+
if (typeof baseSourceText !== 'string' || typeof workerSourceText !== 'string' || typeof headSourceText !== 'string') continue;
|
|
84
|
+
const prepared = prepareRegion({ region, baseSourceText, workerSourceText, headSourceText });
|
|
85
|
+
reasonCodes.push(...prepared.reasonCodes);
|
|
86
|
+
if (prepared.ok) preparedRegions.push(prepared.value);
|
|
87
|
+
}
|
|
88
|
+
const explicitPolicy = policyRegions.length > 0;
|
|
89
|
+
return {
|
|
90
|
+
baseSourceText,
|
|
91
|
+
workerSourceText,
|
|
92
|
+
headSourceText,
|
|
93
|
+
reasonCodes: uniqueStrings(reasonCodes),
|
|
94
|
+
preparedRegions,
|
|
95
|
+
explicitPolicy
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function nonPolicySourceChangeReasons(analysis) {
|
|
100
|
+
const reasonCodes = [];
|
|
101
|
+
const { baseSourceText, workerSourceText, headSourceText, preparedRegions } = analysis;
|
|
102
|
+
if (typeof baseSourceText === 'string' && typeof workerSourceText === 'string' && preparedRegions.length) {
|
|
103
|
+
const canonicalWorker = canonicalizeSourceBodies(workerSourceText, preparedRegions, 'worker');
|
|
104
|
+
if (canonicalWorker !== baseSourceText) reasonCodes.push('non-policy-source-change:worker');
|
|
105
|
+
}
|
|
106
|
+
if (typeof baseSourceText === 'string' && typeof headSourceText === 'string' && preparedRegions.length) {
|
|
107
|
+
const canonicalHead = canonicalizeSourceBodies(headSourceText, preparedRegions, 'head');
|
|
108
|
+
if (canonicalHead !== baseSourceText) reasonCodes.push('non-policy-source-change:head');
|
|
109
|
+
}
|
|
110
|
+
return reasonCodes;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizePolicyRegions(policy) {
|
|
114
|
+
const direct = Array.isArray(policy) ? policy : undefined;
|
|
115
|
+
const regions = direct
|
|
116
|
+
?? policy?.unorderedRegions
|
|
117
|
+
?? policy?.unorderedMemberRegions
|
|
118
|
+
?? policy?.safeList
|
|
119
|
+
?? policy?.safeMembers
|
|
120
|
+
?? [];
|
|
121
|
+
return Array.isArray(regions) ? regions : [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function validatePolicyRegion(region) {
|
|
125
|
+
const reasonCodes = [];
|
|
126
|
+
const kind = normalizeKind(region?.kind);
|
|
127
|
+
if (!kind || !['interface', 'type', 'class', 'object'].includes(kind)) reasonCodes.push('unsupported-region-kind');
|
|
128
|
+
if (!region?.name || typeof region.name !== 'string') reasonCodes.push('missing-region-name');
|
|
129
|
+
if (!regionDeclaresNonSemanticOrder(region)) reasonCodes.push('region-not-declared-non-semantic');
|
|
130
|
+
const regionKind = region?.regionKind;
|
|
131
|
+
if (regionKind && OrderSensitiveRegionKinds.has(regionKind)) reasonCodes.push(`order-sensitive-region-kind:${regionKind}`);
|
|
132
|
+
if (kind === 'object' && (!regionKind || !NonSemanticRegionKinds.has(regionKind))) reasonCodes.push('object-region-kind-not-safe-listed');
|
|
133
|
+
return reasonCodes;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function regionDeclaresNonSemanticOrder(region) {
|
|
137
|
+
return region?.order === 'non-semantic'
|
|
138
|
+
|| region?.ordering === 'non-semantic'
|
|
139
|
+
|| region?.nonSemanticOrder === true
|
|
140
|
+
|| region?.orderSensitive === false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function prepareRegion(input) {
|
|
144
|
+
const kind = normalizeKind(input.region.kind);
|
|
145
|
+
const base = findContainer(input.baseSourceText, input.region);
|
|
146
|
+
const worker = findContainer(input.workerSourceText, input.region);
|
|
147
|
+
const head = findContainer(input.headSourceText, input.region);
|
|
148
|
+
const reasonCodes = [];
|
|
149
|
+
for (const [side, match] of [['base', base], ['worker', worker], ['head', head]]) {
|
|
150
|
+
if (match.reasonCodes.length) reasonCodes.push(...match.reasonCodes.map((reason) => `${reason}:${side}:${kind}:${input.region.name}`));
|
|
151
|
+
}
|
|
152
|
+
if (reasonCodes.length) return { ok: false, reasonCodes };
|
|
153
|
+
const baseMembers = parseMembers(base.value.body, kind);
|
|
154
|
+
const workerMembers = parseMembers(worker.value.body, kind);
|
|
155
|
+
const headMembers = parseMembers(head.value.body, kind);
|
|
156
|
+
reasonCodes.push(...regionParseReasons(input.region, 'base', baseMembers));
|
|
157
|
+
reasonCodes.push(...regionParseReasons(input.region, 'worker', workerMembers));
|
|
158
|
+
reasonCodes.push(...regionParseReasons(input.region, 'head', headMembers));
|
|
159
|
+
reasonCodes.push(...duplicateReasons(input.region, 'base', baseMembers.members));
|
|
160
|
+
reasonCodes.push(...duplicateReasons(input.region, 'worker', workerMembers.members));
|
|
161
|
+
reasonCodes.push(...duplicateReasons(input.region, 'head', headMembers.members));
|
|
162
|
+
if (reasonCodes.length) return { ok: false, reasonCodes };
|
|
163
|
+
const baseByKey = membersByKey(baseMembers.members);
|
|
164
|
+
const workerByKey = membersByKey(workerMembers.members);
|
|
165
|
+
const headByKey = membersByKey(headMembers.members);
|
|
166
|
+
reasonCodes.push(...existingMemberReasons(input.region, 'worker', baseMembers.members, workerMembers.members, workerByKey));
|
|
167
|
+
reasonCodes.push(...existingMemberReasons(input.region, 'head', baseMembers.members, headMembers.members, headByKey));
|
|
168
|
+
const workerAddedKeys = workerMembers.members.map((member) => member.key).filter((key) => !baseByKey.has(key));
|
|
169
|
+
const headAddedKeys = headMembers.members.map((member) => member.key).filter((key) => !baseByKey.has(key));
|
|
170
|
+
reasonCodes.push(...duplicateAddedReasons(input.region, workerAddedKeys, workerByKey, headByKey));
|
|
171
|
+
if (reasonCodes.length) return { ok: false, reasonCodes };
|
|
172
|
+
return {
|
|
173
|
+
ok: true,
|
|
174
|
+
reasonCodes: [],
|
|
175
|
+
value: {
|
|
176
|
+
policy: input.region,
|
|
177
|
+
kind,
|
|
178
|
+
name: input.region.name,
|
|
179
|
+
base: base.value,
|
|
180
|
+
worker: worker.value,
|
|
181
|
+
head: head.value,
|
|
182
|
+
baseMembers: baseMembers.members,
|
|
183
|
+
workerMembers: workerMembers.members,
|
|
184
|
+
headMembers: headMembers.members,
|
|
185
|
+
workerAddedKeys,
|
|
186
|
+
headAddedKeys,
|
|
187
|
+
workerAddedMembers: workerMembers.members.filter((member) => workerAddedKeys.includes(member.key)),
|
|
188
|
+
headAddedMembers: headMembers.members.filter((member) => headAddedKeys.includes(member.key))
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function regionParseReasons(region, side, parsed) {
|
|
194
|
+
return parsed.reasonCodes.map((reason) => regionReason(region, `${reason}:${side}`));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function duplicateReasons(region, side, members) {
|
|
198
|
+
const seen = new Map();
|
|
199
|
+
const duplicateGroups = new Map();
|
|
200
|
+
for (const member of members) {
|
|
201
|
+
const group = seen.get(member.key);
|
|
202
|
+
if (group) {
|
|
203
|
+
group.push(member);
|
|
204
|
+
duplicateGroups.set(member.key, group);
|
|
205
|
+
} else {
|
|
206
|
+
seen.set(member.key, [member]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return [...duplicateGroups].map(([key, group]) => {
|
|
210
|
+
const reason = group.some(isOverloadLikeMember) ? `overload-collision:${side}:${key}` : `duplicate-key:${side}:${key}`;
|
|
211
|
+
return regionReason(region, reason);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function existingMemberReasons(region, side, baseMembers, sideMembers, sideByKey) {
|
|
216
|
+
const reasonCodes = [];
|
|
217
|
+
const sideBaseOrder = sideMembers.map((member) => member.key).filter((key) => baseMembers.some((baseMember) => baseMember.key === key));
|
|
218
|
+
const baseOrder = baseMembers.map((member) => member.key);
|
|
219
|
+
if (sideBaseOrder.join('\u0000') !== baseOrder.join('\u0000')) {
|
|
220
|
+
reasonCodes.push(regionReason(region, `existing-member-order-changed:${side}`));
|
|
221
|
+
}
|
|
222
|
+
for (const baseMember of baseMembers) {
|
|
223
|
+
const sideMember = sideByKey.get(baseMember.key);
|
|
224
|
+
if (!sideMember) {
|
|
225
|
+
reasonCodes.push(regionReason(region, `existing-member-removed:${side}:${baseMember.key}`));
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (normalizeMemberText(sideMember.text) !== normalizeMemberText(baseMember.text)) {
|
|
229
|
+
reasonCodes.push(regionReason(region, `existing-member-changed:${side}:${baseMember.key}`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return reasonCodes;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function duplicateAddedReasons(region, workerAddedKeys, workerByKey, headByKey) {
|
|
236
|
+
const duplicateAddedKeys = workerAddedKeys.filter((key) => headByKey.has(key));
|
|
237
|
+
return duplicateAddedKeys.map((key) => {
|
|
238
|
+
const workerMember = workerByKey.get(key);
|
|
239
|
+
const headMember = headByKey.get(key);
|
|
240
|
+
const reason = isOverloadLikeMember(workerMember) || isOverloadLikeMember(headMember)
|
|
241
|
+
? `overload-collision:worker-head:${key}`
|
|
242
|
+
: `duplicate-added-key:${key}`;
|
|
243
|
+
return regionReason(region, reason);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isOverloadLikeMember(member) {
|
|
248
|
+
return member?.memberKind === 'method' || member?.memberKind === 'constructor';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function membersByKey(members) {
|
|
252
|
+
return new Map(members.map((member) => [member.key, member]));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function regionReason(region, reason) {
|
|
256
|
+
return `${reason}:${normalizeKind(region.kind)}:${region.name}`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export {
|
|
260
|
+
analyzeJsTsSafeMemberAdditions,
|
|
261
|
+
applyJsTsPreparedMemberAdditions,
|
|
262
|
+
mergeJsTsSafeMemberAdditions,
|
|
263
|
+
neutralizeJsTsSafeMemberMergeSources,
|
|
264
|
+
safeMergeJsTsMembers
|
|
265
|
+
};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
|
|
2
|
+
import { addConflict, arraysEqual, sameStatementText } from './js-ts-safe-merge-context.js';
|
|
3
|
+
|
|
4
|
+
export function analyzeVariantLedger(base, variant, baseIndex, side, context) {
|
|
5
|
+
const projectedBaseKeys = [];
|
|
6
|
+
const addedEntries = [];
|
|
7
|
+
const importAdditions = new Map();
|
|
8
|
+
const baseKeys = new Set(baseIndex.orderedKeys);
|
|
9
|
+
|
|
10
|
+
for (const entry of variant.entries) {
|
|
11
|
+
const baseEntry = baseIndex.entriesByKey.get(entry.key);
|
|
12
|
+
if (!baseEntry) {
|
|
13
|
+
if (entry.kind === 'import') {
|
|
14
|
+
addConflict(context, {
|
|
15
|
+
code: JsTsSafeMergeConflictCodes.newImportDeclaration,
|
|
16
|
+
gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
|
|
17
|
+
side,
|
|
18
|
+
message: `${side} source adds a new import declaration; only specifier additions to existing imports are accepted.`,
|
|
19
|
+
details: { key: entry.key, statement: entry.text.trim() }
|
|
20
|
+
});
|
|
21
|
+
} else {
|
|
22
|
+
addedEntries.push(entry);
|
|
23
|
+
}
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
projectedBaseKeys.push(entry.key);
|
|
27
|
+
if (entry.kind !== baseEntry.kind) {
|
|
28
|
+
addConflict(context, {
|
|
29
|
+
code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
|
|
30
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
31
|
+
side,
|
|
32
|
+
message: `${side} source changes a base ledger entry kind.`,
|
|
33
|
+
details: { key: entry.key, baseKind: baseEntry.kind, sideKind: entry.kind }
|
|
34
|
+
});
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (entry.kind === 'import') {
|
|
38
|
+
const additions = analyzeImportStatementChange(baseEntry, entry, side, context);
|
|
39
|
+
if (additions.length) importAdditions.set(entry.key, additions);
|
|
40
|
+
} else if (!sameStatementText(baseEntry.text, entry.text)) {
|
|
41
|
+
const typeAliasConflict = baseEntry.declarationInfo?.declarationKind === 'type'
|
|
42
|
+
|| entry.declarationInfo?.declarationKind === 'type';
|
|
43
|
+
addConflict(context, {
|
|
44
|
+
code: typeAliasConflict ? JsTsSafeMergeConflictCodes.typeAliasConflict : JsTsSafeMergeConflictCodes.changedExistingDeclaration,
|
|
45
|
+
gateId: JsTsSafeMergeGateIds.stableExistingDeclarations,
|
|
46
|
+
side,
|
|
47
|
+
message: typeAliasConflict
|
|
48
|
+
? `${side} source changes an existing type alias body.`
|
|
49
|
+
: `${side} source changes an existing top-level declaration or export body.`,
|
|
50
|
+
details: { key: entry.key, name: entry.names?.[0], declarationKind: entry.declarationInfo?.declarationKind }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!arraysEqual(projectedBaseKeys, baseIndex.orderedKeys)) {
|
|
56
|
+
const code = base.entries.some((entry) => entry.kind === 'import' && entry.importInfo.sideEffectOnly)
|
|
57
|
+
? JsTsSafeMergeConflictCodes.sideEffectImportReorder
|
|
58
|
+
: JsTsSafeMergeConflictCodes.topLevelOrderChanged;
|
|
59
|
+
addConflict(context, {
|
|
60
|
+
code,
|
|
61
|
+
gateId: JsTsSafeMergeGateIds.preserveBaseOrder,
|
|
62
|
+
side,
|
|
63
|
+
message: `${side} source changes the order or presence of base top-level entries.`,
|
|
64
|
+
details: {
|
|
65
|
+
expected: baseIndex.orderedKeys,
|
|
66
|
+
actual: projectedBaseKeys
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
side,
|
|
73
|
+
addedEntries,
|
|
74
|
+
importAdditions,
|
|
75
|
+
baseKeys
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function analyzeImportStatementChange(baseEntry, variantEntry, side, context) {
|
|
80
|
+
const baseImport = baseEntry.importInfo;
|
|
81
|
+
const variantImport = variantEntry.importInfo;
|
|
82
|
+
if (!sameImportShape(baseImport, variantImport)) {
|
|
83
|
+
addConflict(context, {
|
|
84
|
+
code: baseImport.sideEffectOnly ? JsTsSafeMergeConflictCodes.sideEffectImportReorder : JsTsSafeMergeConflictCodes.importShapeChanged,
|
|
85
|
+
gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
|
|
86
|
+
side,
|
|
87
|
+
message: `${side} source changes an existing import shape instead of adding named specifiers.`,
|
|
88
|
+
details: { key: baseEntry.key }
|
|
89
|
+
});
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (baseImport.sideEffectOnly) {
|
|
93
|
+
if (!sameStatementText(baseEntry.text, variantEntry.text)) {
|
|
94
|
+
addConflict(context, {
|
|
95
|
+
code: JsTsSafeMergeConflictCodes.sideEffectImportReorder,
|
|
96
|
+
gateId: JsTsSafeMergeGateIds.preserveBaseOrder,
|
|
97
|
+
side,
|
|
98
|
+
message: `${side} source changes a side-effect import.`,
|
|
99
|
+
details: { key: baseEntry.key }
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const baseSpecifiers = baseImport.specifiers;
|
|
106
|
+
const variantSpecifiers = variantImport.specifiers;
|
|
107
|
+
if (variantSpecifiers.length < baseSpecifiers.length) {
|
|
108
|
+
addConflict(context, {
|
|
109
|
+
code: JsTsSafeMergeConflictCodes.importSpecifierRemoved,
|
|
110
|
+
gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
|
|
111
|
+
side,
|
|
112
|
+
message: `${side} source removes import specifiers.`,
|
|
113
|
+
details: { key: baseEntry.key }
|
|
114
|
+
});
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
for (let index = 0; index < baseSpecifiers.length; index += 1) {
|
|
118
|
+
if (variantSpecifiers[index]?.canonical !== baseSpecifiers[index].canonical) {
|
|
119
|
+
addConflict(context, {
|
|
120
|
+
code: JsTsSafeMergeConflictCodes.importSpecifierReordered,
|
|
121
|
+
gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
|
|
122
|
+
side,
|
|
123
|
+
message: `${side} source reorders or changes existing import specifiers.`,
|
|
124
|
+
details: {
|
|
125
|
+
key: baseEntry.key,
|
|
126
|
+
expected: baseSpecifiers.map((specifier) => specifier.canonical),
|
|
127
|
+
actual: variantSpecifiers.map((specifier) => specifier.canonical)
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const additions = variantSpecifiers.slice(baseSpecifiers.length);
|
|
134
|
+
if (additions.length === 0 && !sameStatementText(baseEntry.text, variantEntry.text)) {
|
|
135
|
+
addConflict(context, {
|
|
136
|
+
code: JsTsSafeMergeConflictCodes.importFormattingChanged,
|
|
137
|
+
gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
|
|
138
|
+
side,
|
|
139
|
+
message: `${side} source changes import formatting without a specifier addition.`,
|
|
140
|
+
details: { key: baseEntry.key }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return additions;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function sameImportShape(left, right) {
|
|
147
|
+
return left.moduleSpecifier === right.moduleSpecifier
|
|
148
|
+
&& left.typeOnly === right.typeOnly
|
|
149
|
+
&& left.sideEffectOnly === right.sideEffectOnly
|
|
150
|
+
&& left.defaultLocalName === right.defaultLocalName
|
|
151
|
+
&& left.namespaceLocalName === right.namespaceLocalName;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function validateIndependentAdditions(base, workerPlan, headPlan, context) {
|
|
155
|
+
const declarationNames = new Set();
|
|
156
|
+
for (const entry of base.entries.filter((item) => item.kind !== 'import')) {
|
|
157
|
+
for (const name of entry.names ?? []) declarationNames.add(`${entry.kind}:${name}`);
|
|
158
|
+
}
|
|
159
|
+
validateAddedEntryNames(workerPlan, declarationNames, context);
|
|
160
|
+
validateAddedEntryNames(headPlan, declarationNames, context);
|
|
161
|
+
validateCrossSideAddedNames(workerPlan, headPlan, context);
|
|
162
|
+
validateCrossSideImportAdditions(workerPlan, headPlan, context);
|
|
163
|
+
validateMergedImportAndDeclarationNames(base, workerPlan, headPlan, context);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function validateAddedEntryNames(plan, baseNames, context) {
|
|
167
|
+
for (const entry of plan.addedEntries) {
|
|
168
|
+
if (entry.kind !== 'declaration' && entry.kind !== 'export') {
|
|
169
|
+
addConflict(context, {
|
|
170
|
+
code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
|
|
171
|
+
gateId: JsTsSafeMergeGateIds.independentTopLevelDeclarations,
|
|
172
|
+
side: plan.side,
|
|
173
|
+
message: `${plan.side} source adds an unsupported top-level entry.`,
|
|
174
|
+
details: { kind: entry.kind, statement: entry.text.trim() }
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (const name of entry.names ?? []) {
|
|
179
|
+
const nameKey = `${entry.kind}:${name}`;
|
|
180
|
+
if (baseNames.has(nameKey)) {
|
|
181
|
+
addConflict(context, {
|
|
182
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
183
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
184
|
+
side: plan.side,
|
|
185
|
+
message: `${plan.side} source adds a duplicate top-level name.`,
|
|
186
|
+
details: { name, kind: entry.kind }
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function validateCrossSideAddedNames(workerPlan, headPlan, context) {
|
|
194
|
+
const headEntriesByName = new Map();
|
|
195
|
+
for (const entry of headPlan.addedEntries) {
|
|
196
|
+
for (const name of entry.names ?? []) headEntriesByName.set(`${entry.kind}:${name}`, entry);
|
|
197
|
+
}
|
|
198
|
+
for (const entry of workerPlan.addedEntries) {
|
|
199
|
+
for (const name of entry.names ?? []) {
|
|
200
|
+
const nameKey = `${entry.kind}:${name}`;
|
|
201
|
+
const headEntry = headEntriesByName.get(nameKey);
|
|
202
|
+
if (headEntry) {
|
|
203
|
+
const typeAliasConflict = entry.declarationInfo?.declarationKind === 'type'
|
|
204
|
+
|| headEntry.declarationInfo?.declarationKind === 'type';
|
|
205
|
+
addConflict(context, {
|
|
206
|
+
code: typeAliasConflict ? JsTsSafeMergeConflictCodes.typeAliasConflict : JsTsSafeMergeConflictCodes.duplicateName,
|
|
207
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
208
|
+
side: 'worker',
|
|
209
|
+
message: typeAliasConflict
|
|
210
|
+
? 'Worker and head add conflicting type aliases.'
|
|
211
|
+
: 'Worker and head add the same top-level name.',
|
|
212
|
+
details: { name, kind: entry.kind, workerDeclarationKind: entry.declarationInfo?.declarationKind, headDeclarationKind: headEntry.declarationInfo?.declarationKind }
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function validateCrossSideImportAdditions(workerPlan, headPlan, context) {
|
|
220
|
+
for (const [key, workerAdditions] of workerPlan.importAdditions) {
|
|
221
|
+
const headAdditions = headPlan.importAdditions.get(key) ?? [];
|
|
222
|
+
const headLocalNames = new Set(headAdditions.map((specifier) => specifier.localName));
|
|
223
|
+
const headImportedNames = new Set(headAdditions.map((specifier) => `${specifier.typeOnly ? 'type:' : 'value:'}${specifier.importedName}`));
|
|
224
|
+
for (const specifier of workerAdditions) {
|
|
225
|
+
if (headLocalNames.has(specifier.localName) || headImportedNames.has(`${specifier.typeOnly ? 'type:' : 'value:'}${specifier.importedName}`)) {
|
|
226
|
+
addConflict(context, {
|
|
227
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
228
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
229
|
+
side: 'worker',
|
|
230
|
+
message: 'Worker and head add duplicate import specifiers.',
|
|
231
|
+
details: { key, specifier: specifier.canonical }
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function validateMergedImportAndDeclarationNames(base, workerPlan, headPlan, context) {
|
|
239
|
+
const topLevelBindingNames = new Set();
|
|
240
|
+
for (const entry of base.entries.filter((item) => item.kind !== 'import')) {
|
|
241
|
+
for (const name of entry.names ?? []) topLevelBindingNames.add(name);
|
|
242
|
+
}
|
|
243
|
+
for (const plan of [workerPlan, headPlan]) {
|
|
244
|
+
for (const entry of plan.addedEntries.filter((item) => item.kind !== 'import')) {
|
|
245
|
+
for (const name of entry.names ?? []) topLevelBindingNames.add(name);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const importLocalNames = new Set();
|
|
249
|
+
for (const entry of base.entries.filter((item) => item.kind === 'import')) {
|
|
250
|
+
const additions = [
|
|
251
|
+
...(workerPlan.importAdditions.get(entry.key) ?? []),
|
|
252
|
+
...(headPlan.importAdditions.get(entry.key) ?? [])
|
|
253
|
+
];
|
|
254
|
+
for (const specifier of [
|
|
255
|
+
...(entry.importInfo.defaultLocalName ? [{ localName: entry.importInfo.defaultLocalName, canonical: `default:${entry.importInfo.defaultLocalName}` }] : []),
|
|
256
|
+
...(entry.importInfo.namespaceLocalName ? [{ localName: entry.importInfo.namespaceLocalName, canonical: `namespace:${entry.importInfo.namespaceLocalName}` }] : []),
|
|
257
|
+
...entry.importInfo.specifiers,
|
|
258
|
+
...additions
|
|
259
|
+
]) {
|
|
260
|
+
if (importLocalNames.has(specifier.localName)) {
|
|
261
|
+
addConflict(context, {
|
|
262
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
263
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
264
|
+
message: 'Merged imports would contain duplicate local names.',
|
|
265
|
+
details: { localName: specifier.localName, specifier: specifier.canonical }
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (topLevelBindingNames.has(specifier.localName)) {
|
|
269
|
+
addConflict(context, {
|
|
270
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
271
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
272
|
+
message: 'Merged imports would duplicate a top-level declaration name.',
|
|
273
|
+
details: { localName: specifier.localName, specifier: specifier.canonical }
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
importLocalNames.add(specifier.localName);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|