@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.104
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 +13 -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 +58 -0
- package/dist/declarations/js-ts-safe-merge.d.ts +120 -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 +202 -0
- package/dist/js-ts-safe-merge-analyze.js +279 -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 +64 -0
- package/dist/js-ts-semantic-merge-member-utils.js +18 -0
- package/dist/js-ts-semantic-merge-parse.js +15 -0
- package/dist/js-ts-semantic-merge.js +21 -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,217 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { idFragment, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
|
|
3
|
+
|
|
4
|
+
export const JsTsSemanticMergeConflictClasses = Object.freeze([
|
|
5
|
+
'same-token-edit',
|
|
6
|
+
'same-region-edit',
|
|
7
|
+
'delete-modify',
|
|
8
|
+
'move-edit',
|
|
9
|
+
'rename-rename',
|
|
10
|
+
'import-order',
|
|
11
|
+
'type-surface-drift',
|
|
12
|
+
'trivia-preservation',
|
|
13
|
+
'behavior-evidence-needed'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const JsTsSemanticMergeGateStatuses = Object.freeze([
|
|
17
|
+
'passed',
|
|
18
|
+
'warning',
|
|
19
|
+
'failed',
|
|
20
|
+
'skipped',
|
|
21
|
+
'blocked'
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
import { actionForConflict, actionForGate, array, booleanValue, compactRecord, firstString, normalizeConflictSides, normalizeGateChecks, normalizeGateStatus, normalizeRisk, numberValue, readinessForGateStatus, readinessForRisk, strings, titleForConflictClass } from './js-ts-semantic-merge-contract-helpers.js';
|
|
25
|
+
|
|
26
|
+
export function createJsTsSemanticMergeConflictExplanation(input = {}, options = {}) {
|
|
27
|
+
const conflictClass = firstString(options.conflictClass, input.conflictClass, input.class, 'same-region-edit');
|
|
28
|
+
const risk = normalizeRisk(firstString(options.risk, input.risk, input.severity)) ?? 'medium';
|
|
29
|
+
const readiness = normalizeSemanticMergeReadiness(firstString(options.readiness, input.readiness)) ?? readinessForRisk(risk);
|
|
30
|
+
const language = firstString(options.language, input.language);
|
|
31
|
+
const sourcePath = firstString(options.sourcePath, input.sourcePath);
|
|
32
|
+
const conflictKeys = uniqueStrings([
|
|
33
|
+
...strings(input.conflictKeys),
|
|
34
|
+
input.conflictKey,
|
|
35
|
+
...strings(options.conflictKeys),
|
|
36
|
+
options.conflictKey
|
|
37
|
+
]);
|
|
38
|
+
const matchIds = uniqueStrings([...strings(input.matchIds), ...strings(options.matchIds)]);
|
|
39
|
+
const editIds = uniqueStrings([...strings(input.editIds), ...strings(options.editIds)]);
|
|
40
|
+
const regionIds = uniqueStrings([...strings(input.regionIds), ...strings(options.regionIds)]);
|
|
41
|
+
const tokenIds = uniqueStrings([...strings(input.tokenIds), ...strings(options.tokenIds)]);
|
|
42
|
+
const triviaIds = uniqueStrings([...strings(input.triviaIds), ...strings(options.triviaIds)]);
|
|
43
|
+
const evidenceIds = uniqueStrings([...strings(input.evidenceIds), ...strings(options.evidenceIds)]);
|
|
44
|
+
const reasonCodes = uniqueStrings([
|
|
45
|
+
...strings(input.reasonCodes),
|
|
46
|
+
...strings(input.reasons),
|
|
47
|
+
...strings(options.reasonCodes),
|
|
48
|
+
`js-ts-conflict:${conflictClass}`
|
|
49
|
+
]);
|
|
50
|
+
const sides = normalizeConflictSides(options.sides ?? input.sides);
|
|
51
|
+
const title = firstString(options.title, input.title, titleForConflictClass(conflictClass));
|
|
52
|
+
const summary = firstString(options.summary, input.summary, input.message);
|
|
53
|
+
const suggestedAction = firstString(options.suggestedAction, input.suggestedAction, actionForConflict({ risk, readiness }));
|
|
54
|
+
const stablePayload = compactRecord({
|
|
55
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictExplanation.v1',
|
|
56
|
+
conflictClass,
|
|
57
|
+
risk,
|
|
58
|
+
readiness,
|
|
59
|
+
language,
|
|
60
|
+
sourcePath,
|
|
61
|
+
title,
|
|
62
|
+
summary,
|
|
63
|
+
conflictKeys,
|
|
64
|
+
matchIds,
|
|
65
|
+
editIds,
|
|
66
|
+
regionIds,
|
|
67
|
+
tokenIds,
|
|
68
|
+
triviaIds,
|
|
69
|
+
evidenceIds,
|
|
70
|
+
reasonCodes,
|
|
71
|
+
sides,
|
|
72
|
+
suggestedAction
|
|
73
|
+
});
|
|
74
|
+
const hash = hashSemanticValue(stablePayload);
|
|
75
|
+
const stableId = firstString(options.stableId, input.stableId)
|
|
76
|
+
|| `js_ts_merge_conflict_${idFragment(firstString(conflictKeys[0], sourcePath, conflictClass))}_${idFragment(hash)}`;
|
|
77
|
+
return compactRecord({
|
|
78
|
+
kind: 'frontier.lang.jsTsSemanticMergeConflictExplanation',
|
|
79
|
+
version: 1,
|
|
80
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictExplanation.v1',
|
|
81
|
+
id: firstString(options.id, input.id, stableId),
|
|
82
|
+
stableId,
|
|
83
|
+
hash,
|
|
84
|
+
conflictClass,
|
|
85
|
+
risk,
|
|
86
|
+
readiness,
|
|
87
|
+
language,
|
|
88
|
+
sourcePath,
|
|
89
|
+
title,
|
|
90
|
+
summary,
|
|
91
|
+
conflictKeys,
|
|
92
|
+
matchIds,
|
|
93
|
+
editIds,
|
|
94
|
+
regionIds,
|
|
95
|
+
tokenIds,
|
|
96
|
+
triviaIds,
|
|
97
|
+
evidenceIds,
|
|
98
|
+
reasonCodes,
|
|
99
|
+
sides,
|
|
100
|
+
suggestedAction,
|
|
101
|
+
metadata: compactRecord({
|
|
102
|
+
normalizedBy: 'frontier-lang-compiler/js-ts-semantic-merge',
|
|
103
|
+
structuralContract: true,
|
|
104
|
+
...(input.metadata ?? {}),
|
|
105
|
+
...(options.metadata ?? {})
|
|
106
|
+
})
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function createJsTsSemanticMergeGateResult(input = {}, options = {}) {
|
|
111
|
+
const gateId = firstString(options.gateId, input.gateId, input.id, 'js-ts-semantic-merge');
|
|
112
|
+
const status = normalizeGateStatus(firstString(options.status, input.status)) ?? 'skipped';
|
|
113
|
+
const conflicts = [
|
|
114
|
+
...array(input.conflictExplanations),
|
|
115
|
+
...array(input.conflicts),
|
|
116
|
+
...array(options.conflictExplanations),
|
|
117
|
+
...array(options.conflicts)
|
|
118
|
+
].filter(Boolean).map((conflict) => createJsTsSemanticMergeConflictExplanation(conflict));
|
|
119
|
+
const conflictExplanationIds = uniqueStrings([
|
|
120
|
+
...strings(input.conflictExplanationIds),
|
|
121
|
+
...strings(options.conflictExplanationIds),
|
|
122
|
+
...conflicts.map((conflict) => conflict.id)
|
|
123
|
+
]);
|
|
124
|
+
const checks = normalizeGateChecks([
|
|
125
|
+
...array(input.checks),
|
|
126
|
+
...array(options.checks)
|
|
127
|
+
], status);
|
|
128
|
+
const readiness = normalizeSemanticMergeReadiness(firstString(options.readiness, input.readiness)) ?? readinessForGateStatus(status, checks, conflictExplanationIds);
|
|
129
|
+
const evidenceIds = uniqueStrings([
|
|
130
|
+
...strings(input.evidenceIds),
|
|
131
|
+
...strings(options.evidenceIds),
|
|
132
|
+
...checks.flatMap((check) => strings(check.evidenceIds)),
|
|
133
|
+
...conflicts.flatMap((conflict) => conflict.evidenceIds)
|
|
134
|
+
]);
|
|
135
|
+
const reasonCodes = uniqueStrings([
|
|
136
|
+
...strings(input.reasonCodes),
|
|
137
|
+
...strings(options.reasonCodes),
|
|
138
|
+
...checks.flatMap((check) => strings(check.reasonCodes)),
|
|
139
|
+
...conflicts.flatMap((conflict) => conflict.reasonCodes),
|
|
140
|
+
...(status === 'passed' ? [] : [`js-ts-merge-gate:${status}`])
|
|
141
|
+
]);
|
|
142
|
+
const structuredEditIds = uniqueStrings([
|
|
143
|
+
...strings(input.structuredEditIds),
|
|
144
|
+
...strings(options.structuredEditIds),
|
|
145
|
+
...array(input.structuredEdits).map((edit) => edit?.id),
|
|
146
|
+
...array(options.structuredEdits).map((edit) => edit?.id)
|
|
147
|
+
]);
|
|
148
|
+
const semanticRegionIds = uniqueStrings([
|
|
149
|
+
...strings(input.semanticRegionIds),
|
|
150
|
+
...strings(options.semanticRegionIds),
|
|
151
|
+
...array(input.semanticRegions).map((region) => region?.id),
|
|
152
|
+
...array(options.semanticRegions).map((region) => region?.id)
|
|
153
|
+
]);
|
|
154
|
+
const mergeable = booleanValue(options.mergeable, input.mergeable)
|
|
155
|
+
?? (status === 'passed' && readiness === 'ready' && conflictExplanationIds.length === 0);
|
|
156
|
+
const action = firstString(options.action, input.action, actionForGate({ status, readiness, mergeable }));
|
|
157
|
+
const confidence = numberValue(options.confidence, input.confidence, status === 'passed' ? 1 : 0);
|
|
158
|
+
const failedChecks = checks.filter((check) => check.status === 'failed').length;
|
|
159
|
+
const warningChecks = checks.filter((check) => check.status === 'warning').length;
|
|
160
|
+
const blockedChecks = checks.filter((check) => check.status === 'blocked').length;
|
|
161
|
+
const summary = {
|
|
162
|
+
checks: checks.length,
|
|
163
|
+
conflicts: conflictExplanationIds.length,
|
|
164
|
+
failedChecks,
|
|
165
|
+
warningChecks,
|
|
166
|
+
blockedChecks,
|
|
167
|
+
evidenceIds: evidenceIds.length,
|
|
168
|
+
reasonCodes
|
|
169
|
+
};
|
|
170
|
+
const stablePayload = compactRecord({
|
|
171
|
+
schema: 'frontier.lang.jsTsSemanticMergeGateResult.v1',
|
|
172
|
+
gateId,
|
|
173
|
+
status,
|
|
174
|
+
readiness,
|
|
175
|
+
mergeable,
|
|
176
|
+
action,
|
|
177
|
+
confidence,
|
|
178
|
+
checks,
|
|
179
|
+
conflictExplanationIds,
|
|
180
|
+
structuredEditIds,
|
|
181
|
+
semanticRegionIds,
|
|
182
|
+
evidenceIds,
|
|
183
|
+
reasonCodes,
|
|
184
|
+
summary
|
|
185
|
+
});
|
|
186
|
+
const hash = hashSemanticValue(stablePayload);
|
|
187
|
+
const stableId = firstString(options.stableId, input.stableId)
|
|
188
|
+
|| `js_ts_merge_gate_${idFragment(gateId)}_${idFragment(hash)}`;
|
|
189
|
+
return compactRecord({
|
|
190
|
+
kind: 'frontier.lang.jsTsSemanticMergeGateResult',
|
|
191
|
+
version: 1,
|
|
192
|
+
schema: 'frontier.lang.jsTsSemanticMergeGateResult.v1',
|
|
193
|
+
id: firstString(options.id, input.id, stableId),
|
|
194
|
+
stableId,
|
|
195
|
+
hash,
|
|
196
|
+
gateId,
|
|
197
|
+
status,
|
|
198
|
+
readiness,
|
|
199
|
+
mergeable,
|
|
200
|
+
action,
|
|
201
|
+
confidence,
|
|
202
|
+
checks,
|
|
203
|
+
conflictExplanations: conflicts,
|
|
204
|
+
conflictExplanationIds,
|
|
205
|
+
structuredEditIds,
|
|
206
|
+
semanticRegionIds,
|
|
207
|
+
evidenceIds,
|
|
208
|
+
reasonCodes,
|
|
209
|
+
summary,
|
|
210
|
+
metadata: compactRecord({
|
|
211
|
+
normalizedBy: 'frontier-lang-compiler/js-ts-semantic-merge',
|
|
212
|
+
structuralContract: true,
|
|
213
|
+
...(input.metadata ?? {}),
|
|
214
|
+
...(options.metadata ?? {})
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
function normalizeKind(kind) {
|
|
2
|
+
const text = String(kind ?? '').toLowerCase();
|
|
3
|
+
if (text === 'interface') return 'interface';
|
|
4
|
+
if (text === 'type' || text === 'typealias' || text === 'type-alias') return 'type';
|
|
5
|
+
if (text === 'class') return 'class';
|
|
6
|
+
if (text === 'object' || text === 'object-literal') return 'object';
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function findContainer(sourceText, region) {
|
|
11
|
+
const kind = normalizeKind(region.kind);
|
|
12
|
+
const pattern = containerPattern(kind, region.name);
|
|
13
|
+
const matches = [];
|
|
14
|
+
let match;
|
|
15
|
+
while ((match = pattern.exec(sourceText))) {
|
|
16
|
+
const openIndex = sourceText.indexOf('{', match.index);
|
|
17
|
+
if (openIndex < 0 || openIndex >= pattern.lastIndex) continue;
|
|
18
|
+
const closeIndex = findMatchingBrace(sourceText, openIndex);
|
|
19
|
+
if (closeIndex < 0) return { reasonCodes: ['unterminated-container'] };
|
|
20
|
+
matches.push({
|
|
21
|
+
kind,
|
|
22
|
+
name: region.name,
|
|
23
|
+
start: match.index,
|
|
24
|
+
openStart: openIndex,
|
|
25
|
+
bodyStart: openIndex + 1,
|
|
26
|
+
bodyEnd: closeIndex,
|
|
27
|
+
end: closeIndex + 1,
|
|
28
|
+
body: sourceText.slice(openIndex + 1, closeIndex)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (!matches.length) return { reasonCodes: ['container-not-found'] };
|
|
32
|
+
if (matches.length > 1) return { reasonCodes: ['ambiguous-container'] };
|
|
33
|
+
return { reasonCodes: [], value: matches[0] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function containerPattern(kind, name) {
|
|
37
|
+
const escapedName = escapeRegExp(name);
|
|
38
|
+
if (kind === 'interface') return new RegExp(`\\b(?:export\\s+)?interface\\s+${escapedName}\\b[^{};=]*\\{`, 'g');
|
|
39
|
+
if (kind === 'type') return new RegExp(`\\b(?:export\\s+)?type\\s+${escapedName}\\b\\s*=\\s*\\{`, 'g');
|
|
40
|
+
if (kind === 'class') return new RegExp(`\\b(?:export\\s+)?(?:default\\s+)?(?:abstract\\s+)?class\\s+${escapedName}\\b[^{};=]*\\{`, 'g');
|
|
41
|
+
return new RegExp(`\\b(?:export\\s+)?(?:const|let|var)\\s+${escapedName}\\b[^=;{}]*=\\s*\\{`, 'g');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function findMatchingBrace(sourceText, openIndex) {
|
|
45
|
+
let depth = 0;
|
|
46
|
+
let quote;
|
|
47
|
+
let escaped = false;
|
|
48
|
+
let blockComment = false;
|
|
49
|
+
let lineComment = false;
|
|
50
|
+
for (let index = openIndex; index < sourceText.length; index += 1) {
|
|
51
|
+
const char = sourceText[index];
|
|
52
|
+
const next = sourceText[index + 1];
|
|
53
|
+
if (lineComment) {
|
|
54
|
+
if (char === '\n' || char === '\r') lineComment = false;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (blockComment) {
|
|
58
|
+
if (char === '*' && next === '/') {
|
|
59
|
+
blockComment = false;
|
|
60
|
+
index += 1;
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (quote) {
|
|
65
|
+
if (escaped) escaped = false;
|
|
66
|
+
else if (char === '\\') escaped = true;
|
|
67
|
+
else if (char === quote) quote = undefined;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (char === '/' && next === '/') {
|
|
71
|
+
lineComment = true;
|
|
72
|
+
index += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (char === '/' && next === '*') {
|
|
76
|
+
blockComment = true;
|
|
77
|
+
index += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (char === '\'' || char === '"' || char === '`') {
|
|
81
|
+
quote = char;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (char === '{') depth += 1;
|
|
85
|
+
else if (char === '}') {
|
|
86
|
+
depth -= 1;
|
|
87
|
+
if (depth === 0) return index;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function escapeRegExp(value) {
|
|
94
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
findContainer,
|
|
99
|
+
normalizeKind
|
|
100
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
function memberKey(text, kind) {
|
|
2
|
+
if (kind === 'object') return objectMemberKey(text);
|
|
3
|
+
if (kind === 'class') return classMemberKey(text);
|
|
4
|
+
return typeMemberKey(text);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function objectMemberKey(text) {
|
|
8
|
+
const source = text.replace(/[;,]\s*$/, '').trim();
|
|
9
|
+
const methodSource = stripObjectMemberPrefixes(source);
|
|
10
|
+
const methodMatch = methodSource.match(/^(['"]?)([A-Za-z_$][\w$-]*)\1\s*(?:<[^({;]+>)?\s*\(/);
|
|
11
|
+
if (methodMatch) return { key: methodMatch[2], memberKind: 'method' };
|
|
12
|
+
const colon = topLevelColon(text);
|
|
13
|
+
if (colon >= 0) {
|
|
14
|
+
const key = propertyKey(text.slice(0, colon).trim());
|
|
15
|
+
return key ? { key, memberKind: 'property' } : undefined;
|
|
16
|
+
}
|
|
17
|
+
const shorthand = propertyKey(source);
|
|
18
|
+
return shorthand ? { key: shorthand, memberKind: 'property' } : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function typeMemberKey(text) {
|
|
22
|
+
const source = text.replace(/[;,]\s*$/, '').trim().replace(/^readonly\s+/, '');
|
|
23
|
+
const methodMatch = source.match(/^(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*(?:<[^({;]+>)?\s*\(/);
|
|
24
|
+
if (methodMatch) return { key: methodMatch[2], memberKind: 'method' };
|
|
25
|
+
const colon = topLevelColon(source);
|
|
26
|
+
if (colon < 0) return undefined;
|
|
27
|
+
const key = propertyKey(source.slice(0, colon).trim().replace(/\?$/, '').trim());
|
|
28
|
+
return key ? { key, memberKind: 'property' } : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function classMemberKey(text) {
|
|
32
|
+
const stripped = stripClassMemberPrefixes(text.replace(/[;,]\s*$/, '').trim());
|
|
33
|
+
const source = stripped.source;
|
|
34
|
+
if (!source || source.startsWith('@') || /^static\s*\{/.test(source)) return undefined;
|
|
35
|
+
const constructorMatch = source.match(/^constructor\s*\(/);
|
|
36
|
+
if (constructorMatch) return { key: 'constructor', memberKind: 'constructor' };
|
|
37
|
+
const accessorMatch = source.match(/^(get|set)\s+(['"]?)(#?[A-Za-z_$][\w$-]*)\2\s*\(/);
|
|
38
|
+
if (accessorMatch) return { key: classMemberKeyName(accessorMatch[3], stripped.isStatic), memberKind: 'accessor' };
|
|
39
|
+
const methodMatch = source.match(/^(?:async\s+)?(['"]?)(#?[A-Za-z_$][\w$-]*)\1\s*(?:<[^({;]+>)?\s*\(/);
|
|
40
|
+
if (methodMatch) return { key: classMemberKeyName(methodMatch[2], stripped.isStatic), memberKind: 'method' };
|
|
41
|
+
const propertyMatch = source.match(/^(['"]?)(#?[A-Za-z_$][\w$-]*)\1[?!]?\s*(?::|=|$)/);
|
|
42
|
+
if (propertyMatch) return { key: classMemberKeyName(propertyMatch[2], stripped.isStatic), memberKind: 'property' };
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function classMemberKeyName(name, isStatic) {
|
|
47
|
+
return isStatic ? `static.${name}` : name;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function stripObjectMemberPrefixes(source) {
|
|
51
|
+
return String(source ?? '').replace(/^(?:async|get|set)\s+/, '');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function stripClassMemberPrefixes(source) {
|
|
55
|
+
let output = String(source ?? '').trim();
|
|
56
|
+
let isStatic = false;
|
|
57
|
+
const prefixes = ['public', 'private', 'protected', 'abstract', 'override', 'declare', 'readonly', 'accessor'];
|
|
58
|
+
let changed = true;
|
|
59
|
+
while (changed) {
|
|
60
|
+
changed = false;
|
|
61
|
+
for (const prefix of prefixes) {
|
|
62
|
+
const pattern = new RegExp(`^${prefix}\\s+`);
|
|
63
|
+
if (pattern.test(output)) {
|
|
64
|
+
output = output.replace(pattern, '').trimStart();
|
|
65
|
+
changed = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (/^static\s+/.test(output)) {
|
|
69
|
+
output = output.replace(/^static\s+/, '').trimStart();
|
|
70
|
+
isStatic = true;
|
|
71
|
+
changed = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { source: output, isStatic };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isTypeAliasConflictMember(text) {
|
|
78
|
+
return /^\|/.test(text) || /^&/.test(text);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function hasSpreadLikeMember(text) {
|
|
82
|
+
return stripLeadingMemberKeywords(text).startsWith('...');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function hasComputedMemberKey(text) {
|
|
86
|
+
return stripLeadingMemberKeywords(text).startsWith('[');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function stripLeadingMemberKeywords(text) {
|
|
90
|
+
let source = String(text ?? '').trim();
|
|
91
|
+
const prefixes = ['public', 'private', 'protected', 'abstract', 'override', 'declare', 'readonly', 'static', 'accessor', 'async', 'get', 'set'];
|
|
92
|
+
let changed = true;
|
|
93
|
+
while (changed) {
|
|
94
|
+
changed = false;
|
|
95
|
+
for (const prefix of prefixes) {
|
|
96
|
+
const pattern = new RegExp(`^${prefix}\\s+`);
|
|
97
|
+
if (pattern.test(source)) {
|
|
98
|
+
source = source.replace(pattern, '').trimStart();
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return source;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function propertyKey(source) {
|
|
107
|
+
if (!source || source.startsWith('[')) return undefined;
|
|
108
|
+
const quoted = source.match(/^(['"])([^'"\\]+)\1$/);
|
|
109
|
+
if (quoted) return quoted[2];
|
|
110
|
+
const identifier = source.match(/^[A-Za-z_$][\w$-]*$/);
|
|
111
|
+
return identifier?.[0];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function topLevelColon(source) {
|
|
115
|
+
let quote;
|
|
116
|
+
let escaped = false;
|
|
117
|
+
let depth = 0;
|
|
118
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
119
|
+
const char = source[index];
|
|
120
|
+
if (quote) {
|
|
121
|
+
if (escaped) escaped = false;
|
|
122
|
+
else if (char === '\\') escaped = true;
|
|
123
|
+
else if (char === quote) quote = undefined;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (char === '\'' || char === '"' || char === '`') {
|
|
127
|
+
quote = char;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (char === '{' || char === '[' || char === '(') depth += 1;
|
|
131
|
+
else if (char === '}' || char === ']' || char === ')') depth -= 1;
|
|
132
|
+
else if (char === ':' && depth === 0) return index;
|
|
133
|
+
}
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
hasComputedMemberKey,
|
|
139
|
+
hasSpreadLikeMember,
|
|
140
|
+
isTypeAliasConflictMember,
|
|
141
|
+
memberKey
|
|
142
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasComputedMemberKey,
|
|
3
|
+
hasSpreadLikeMember,
|
|
4
|
+
isTypeAliasConflictMember,
|
|
5
|
+
memberKey
|
|
6
|
+
} from './js-ts-semantic-merge-member-keys.js';
|
|
7
|
+
import { uniqueStrings } from './js-ts-semantic-merge-member-utils.js';
|
|
8
|
+
|
|
9
|
+
function parseMembers(body, kind) {
|
|
10
|
+
const reasonCodes = [];
|
|
11
|
+
const members = [];
|
|
12
|
+
for (const segment of splitMemberSegments(body, kind)) {
|
|
13
|
+
const trimmed = segment.text.trim();
|
|
14
|
+
if (!trimmed) continue;
|
|
15
|
+
if (hasSpreadLikeMember(trimmed)) {
|
|
16
|
+
reasonCodes.push('spread-like-member');
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (hasComputedMemberKey(trimmed)) {
|
|
20
|
+
reasonCodes.push('computed-key');
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (kind === 'type' && isTypeAliasConflictMember(trimmed)) {
|
|
24
|
+
reasonCodes.push('type-alias-conflict');
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const parsed = memberKey(trimmed, kind);
|
|
28
|
+
if (!parsed?.key) {
|
|
29
|
+
reasonCodes.push(`unsupported-${kind}-member`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
members.push({ ...segment, ...parsed });
|
|
33
|
+
}
|
|
34
|
+
return { members, reasonCodes: uniqueStrings(reasonCodes) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function splitMemberSegments(body, kind) {
|
|
38
|
+
if (kind === 'class') return splitClassMemberSegments(body);
|
|
39
|
+
const entries = [];
|
|
40
|
+
const text = String(body ?? '');
|
|
41
|
+
let start = 0;
|
|
42
|
+
let quote;
|
|
43
|
+
let escaped = false;
|
|
44
|
+
let blockComment = false;
|
|
45
|
+
let lineComment = false;
|
|
46
|
+
let depth = 0;
|
|
47
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
48
|
+
const char = text[index];
|
|
49
|
+
const next = text[index + 1];
|
|
50
|
+
if (lineComment) {
|
|
51
|
+
if (char === '\n' || char === '\r') lineComment = false;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (blockComment) {
|
|
55
|
+
if (char === '*' && next === '/') {
|
|
56
|
+
blockComment = false;
|
|
57
|
+
index += 1;
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (quote) {
|
|
62
|
+
if (escaped) escaped = false;
|
|
63
|
+
else if (char === '\\') escaped = true;
|
|
64
|
+
else if (char === quote) quote = undefined;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (char === '/' && next === '/') {
|
|
68
|
+
lineComment = true;
|
|
69
|
+
index += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (char === '/' && next === '*') {
|
|
73
|
+
blockComment = true;
|
|
74
|
+
index += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (char === '\'' || char === '"' || char === '`') {
|
|
78
|
+
quote = char;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (char === '{' || char === '[' || char === '(') depth += 1;
|
|
82
|
+
else if (char === '}' || char === ']' || char === ')') depth -= 1;
|
|
83
|
+
const separator = kind === 'object' ? ',' : ';';
|
|
84
|
+
if (char === separator && depth === 0) {
|
|
85
|
+
entries.push({ text: text.slice(start, index + 1), start, end: index + 1 });
|
|
86
|
+
start = index + 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
entries.push({ text: text.slice(start), start, end: text.length });
|
|
90
|
+
return entries.filter((entry) => entry.text.trim());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function splitClassMemberSegments(body) {
|
|
94
|
+
const entries = [];
|
|
95
|
+
const text = String(body ?? '');
|
|
96
|
+
let start = 0;
|
|
97
|
+
let quote;
|
|
98
|
+
let escaped = false;
|
|
99
|
+
let blockComment = false;
|
|
100
|
+
let lineComment = false;
|
|
101
|
+
let depth = 0;
|
|
102
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
103
|
+
const char = text[index];
|
|
104
|
+
const next = text[index + 1];
|
|
105
|
+
if (lineComment) {
|
|
106
|
+
if (char === '\n' || char === '\r') lineComment = false;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (blockComment) {
|
|
110
|
+
if (char === '*' && next === '/') {
|
|
111
|
+
blockComment = false;
|
|
112
|
+
index += 1;
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (quote) {
|
|
117
|
+
if (escaped) escaped = false;
|
|
118
|
+
else if (char === '\\') escaped = true;
|
|
119
|
+
else if (char === quote) quote = undefined;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (char === '/' && next === '/') {
|
|
123
|
+
lineComment = true;
|
|
124
|
+
index += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (char === '/' && next === '*') {
|
|
128
|
+
blockComment = true;
|
|
129
|
+
index += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (char === '\'' || char === '"' || char === '`') {
|
|
133
|
+
quote = char;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (char === '{' || char === '[' || char === '(') {
|
|
137
|
+
depth += 1;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (char === '}' || char === ']' || char === ')') {
|
|
141
|
+
depth -= 1;
|
|
142
|
+
if (depth === 0 && char === '}') {
|
|
143
|
+
const nextIndex = nextSignificantIndex(text, index + 1);
|
|
144
|
+
if (text[nextIndex] !== ';') {
|
|
145
|
+
entries.push({ text: text.slice(start, index + 1), start, end: index + 1 });
|
|
146
|
+
start = index + 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (char === ';' && depth === 0) {
|
|
152
|
+
entries.push({ text: text.slice(start, index + 1), start, end: index + 1 });
|
|
153
|
+
start = index + 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
entries.push({ text: text.slice(start), start, end: text.length });
|
|
157
|
+
return entries.filter((entry) => entry.text.trim());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function nextSignificantIndex(text, offset) {
|
|
161
|
+
let index = offset;
|
|
162
|
+
while (index < text.length) {
|
|
163
|
+
if (/\s/.test(text[index])) {
|
|
164
|
+
index += 1;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (text[index] === '/' && text[index + 1] === '/') {
|
|
168
|
+
index += 2;
|
|
169
|
+
while (index < text.length && text[index] !== '\n' && text[index] !== '\r') index += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (text[index] === '/' && text[index + 1] === '*') {
|
|
173
|
+
index += 2;
|
|
174
|
+
while (index < text.length && !(text[index] === '*' && text[index + 1] === '/')) index += 1;
|
|
175
|
+
index += 2;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
return index;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
parseMembers
|
|
185
|
+
};
|