@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,92 @@
|
|
|
1
|
+
import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
|
|
2
|
+
import { addConflict } from './js-ts-safe-merge-context.js';
|
|
3
|
+
|
|
4
|
+
export function validateLedgerUniqueness(ledger, context) {
|
|
5
|
+
const keyOwners = new Map();
|
|
6
|
+
for (const entry of ledger.entries) {
|
|
7
|
+
const existing = keyOwners.get(entry.key);
|
|
8
|
+
if (existing) {
|
|
9
|
+
addConflict(context, {
|
|
10
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
11
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
12
|
+
side: ledger.label,
|
|
13
|
+
message: `${ledger.label} source has duplicate top-level ledger keys.`,
|
|
14
|
+
details: { key: entry.key, firstOffset: existing.start, secondOffset: entry.start }
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
keyOwners.set(entry.key, entry);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nameOwners = new Map();
|
|
22
|
+
for (const entry of ledger.entries) {
|
|
23
|
+
for (const name of entry.names ?? []) {
|
|
24
|
+
const nameKey = `${entry.kind}:${name}`;
|
|
25
|
+
const existing = nameOwners.get(nameKey);
|
|
26
|
+
if (existing) {
|
|
27
|
+
addConflict(context, {
|
|
28
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
29
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
30
|
+
side: ledger.label,
|
|
31
|
+
message: `${ledger.label} source has duplicate top-level names.`,
|
|
32
|
+
details: { name, firstOffset: existing.start, secondOffset: entry.start }
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
nameOwners.set(nameKey, entry);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const entry of ledger.entries.filter((item) => item.kind === 'import')) {
|
|
41
|
+
validateUniqueImportSpecifiers(entry, ledger.label, context);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function validateUniqueImportSpecifiers(entry, side, context) {
|
|
46
|
+
const localNames = new Map();
|
|
47
|
+
const specifiers = [
|
|
48
|
+
...(entry.importInfo.defaultLocalName ? [{ localName: entry.importInfo.defaultLocalName, canonical: `default:${entry.importInfo.defaultLocalName}` }] : []),
|
|
49
|
+
...(entry.importInfo.namespaceLocalName ? [{ localName: entry.importInfo.namespaceLocalName, canonical: `namespace:${entry.importInfo.namespaceLocalName}` }] : []),
|
|
50
|
+
...entry.importInfo.specifiers
|
|
51
|
+
];
|
|
52
|
+
for (const specifier of specifiers) {
|
|
53
|
+
const existing = localNames.get(specifier.localName);
|
|
54
|
+
if (existing) {
|
|
55
|
+
addConflict(context, {
|
|
56
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
57
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
58
|
+
side,
|
|
59
|
+
message: `${side} source has duplicate import binding names.`,
|
|
60
|
+
details: { importKey: entry.key, localName: specifier.localName, first: existing.canonical, second: specifier.canonical }
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
localNames.set(specifier.localName, specifier);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function indexBaseLedger(base, context) {
|
|
69
|
+
const entriesByKey = new Map();
|
|
70
|
+
for (const entry of base.entries) entriesByKey.set(entry.key, entry);
|
|
71
|
+
const names = new Set();
|
|
72
|
+
for (const entry of base.entries) {
|
|
73
|
+
for (const name of entry.names ?? []) {
|
|
74
|
+
const nameKey = `${entry.kind}:${name}`;
|
|
75
|
+
if (names.has(nameKey)) {
|
|
76
|
+
addConflict(context, {
|
|
77
|
+
code: JsTsSafeMergeConflictCodes.duplicateName,
|
|
78
|
+
gateId: JsTsSafeMergeGateIds.uniqueNames,
|
|
79
|
+
side: 'base',
|
|
80
|
+
message: 'Base source has duplicate declaration or export names.',
|
|
81
|
+
details: { name }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
names.add(nameKey);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
entriesByKey,
|
|
89
|
+
orderedKeys: base.entries.map((entry) => entry.key),
|
|
90
|
+
names
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
|
|
2
|
+
import { addConflict } from './js-ts-safe-merge-context.js';
|
|
3
|
+
import { classifyStatement, importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
|
|
4
|
+
import { findStatementEnd, skipTopLevelTrivia } from './js-ts-safe-merge-parse-statements.js';
|
|
5
|
+
|
|
6
|
+
export function scanJsTsTopLevelLedger(sourceText, label, context) {
|
|
7
|
+
const entries = [];
|
|
8
|
+
let offset = 0;
|
|
9
|
+
while (offset < sourceText.length) {
|
|
10
|
+
const skipped = skipTopLevelTrivia(sourceText, offset);
|
|
11
|
+
if (skipped.error) {
|
|
12
|
+
addConflict(context, {
|
|
13
|
+
code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
|
|
14
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
15
|
+
side: label,
|
|
16
|
+
message: `${label} source contains unterminated top-level trivia.`,
|
|
17
|
+
details: { offset, error: skipped.error }
|
|
18
|
+
});
|
|
19
|
+
return createLedger(label, sourceText, entries);
|
|
20
|
+
}
|
|
21
|
+
offset = skipped.offset;
|
|
22
|
+
if (offset >= sourceText.length) break;
|
|
23
|
+
|
|
24
|
+
if (sourceText[offset] === '@') {
|
|
25
|
+
addConflict(context, {
|
|
26
|
+
code: JsTsSafeMergeConflictCodes.unsupportedDecorator,
|
|
27
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
28
|
+
side: label,
|
|
29
|
+
message: `${label} source contains decorator syntax that is not safe for ledger merge anchors.`,
|
|
30
|
+
details: { offset, reasonCode: 'unsupported-js-ts-syntax', preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
|
|
31
|
+
});
|
|
32
|
+
return createLedger(label, sourceText, entries);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const end = findStatementEnd(sourceText, offset);
|
|
36
|
+
if (end.error) {
|
|
37
|
+
addConflict(context, {
|
|
38
|
+
code: JsTsSafeMergeConflictCodes.malformedSyntax,
|
|
39
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
40
|
+
side: label,
|
|
41
|
+
message: `${label} source contains a top-level statement the narrow ledger cannot bound.`,
|
|
42
|
+
details: { offset, error: end.error, preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
|
|
43
|
+
});
|
|
44
|
+
return createLedger(label, sourceText, entries);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const statementText = sourceText.slice(offset, end.offset);
|
|
48
|
+
const entry = classifyStatement(statementText, offset, end.offset);
|
|
49
|
+
if (entry?.unsupported) {
|
|
50
|
+
addConflict(context, {
|
|
51
|
+
code: entry.unsupported.code,
|
|
52
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
53
|
+
side: label,
|
|
54
|
+
message: `${label} source contains syntax that is not safe for automatic JS/TS ledger merge.`,
|
|
55
|
+
details: { offset, ...entry.unsupported.details, statement: statementText.trim().slice(0, 120) }
|
|
56
|
+
});
|
|
57
|
+
return createLedger(label, sourceText, entries);
|
|
58
|
+
}
|
|
59
|
+
if (!entry) {
|
|
60
|
+
addConflict(context, {
|
|
61
|
+
code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
|
|
62
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
63
|
+
side: label,
|
|
64
|
+
message: `${label} source contains an unsupported top-level statement.`,
|
|
65
|
+
details: { offset, statement: statementText.trim().slice(0, 120) }
|
|
66
|
+
});
|
|
67
|
+
return createLedger(label, sourceText, entries);
|
|
68
|
+
}
|
|
69
|
+
entries.push(entry);
|
|
70
|
+
offset = end.offset;
|
|
71
|
+
}
|
|
72
|
+
return createLedger(label, sourceText, entries);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function createLedger(label, sourceText, entries) {
|
|
76
|
+
return {
|
|
77
|
+
label,
|
|
78
|
+
sourceText,
|
|
79
|
+
entries,
|
|
80
|
+
baseEntries: entries.filter((entry) => entry.kind === 'import' || entry.kind === 'declaration' || entry.kind === 'export')
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
|
|
85
|
+
export { indexBaseLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger-validation.js';
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { JsTsSafeMergeConflictCodes, identifierRegExp } from './js-ts-safe-merge-constants.js';
|
|
2
|
+
|
|
3
|
+
export function classifyStatement(text, start, end) {
|
|
4
|
+
const importInfo = parseImportInfo(text);
|
|
5
|
+
if (importInfo) {
|
|
6
|
+
return {
|
|
7
|
+
kind: 'import',
|
|
8
|
+
key: importLedgerKey(importInfo),
|
|
9
|
+
text,
|
|
10
|
+
start,
|
|
11
|
+
end,
|
|
12
|
+
importInfo,
|
|
13
|
+
names: importInfo.specifiers.map((specifier) => specifier.localName).filter(Boolean)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const declarationInfo = parseDeclarationInfo(text);
|
|
17
|
+
if (declarationInfo) {
|
|
18
|
+
const unsupported = unsupportedDeclarationPolicy(text, declarationInfo);
|
|
19
|
+
if (unsupported) return { unsupported, text, start, end };
|
|
20
|
+
return {
|
|
21
|
+
kind: declarationInfo.kind,
|
|
22
|
+
key: `${declarationInfo.kind}:${declarationInfo.names.join('|')}`,
|
|
23
|
+
text,
|
|
24
|
+
start,
|
|
25
|
+
end,
|
|
26
|
+
declarationInfo,
|
|
27
|
+
names: declarationInfo.names
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseImportInfo(text) {
|
|
34
|
+
const trimmed = text.trim();
|
|
35
|
+
const sideEffect = trimmed.match(/^import\s+(['"])([^'"]+)\1\s*;?$/s);
|
|
36
|
+
if (sideEffect) {
|
|
37
|
+
return {
|
|
38
|
+
moduleSpecifier: sideEffect[2],
|
|
39
|
+
quote: sideEffect[1],
|
|
40
|
+
typeOnly: false,
|
|
41
|
+
sideEffectOnly: true,
|
|
42
|
+
defaultLocalName: undefined,
|
|
43
|
+
namespaceLocalName: undefined,
|
|
44
|
+
specifiers: []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const match = trimmed.match(/^import\s+(type\s+)?([\s\S]+?)\s+from\s+(['"])([^'"]+)\3\s*;?$/s);
|
|
49
|
+
if (!match) return undefined;
|
|
50
|
+
const typeOnly = Boolean(match[1]);
|
|
51
|
+
const clause = match[2].trim();
|
|
52
|
+
const parsedClause = parseImportClause(clause, { typeOnly });
|
|
53
|
+
if (!parsedClause) return undefined;
|
|
54
|
+
return {
|
|
55
|
+
moduleSpecifier: match[4],
|
|
56
|
+
quote: match[3],
|
|
57
|
+
typeOnly,
|
|
58
|
+
sideEffectOnly: false,
|
|
59
|
+
...parsedClause
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseImportClause(clause, options) {
|
|
64
|
+
const namespace = clause.match(/^\*\s+as\s+([A-Za-z_$][\w$]*)$/);
|
|
65
|
+
if (namespace) {
|
|
66
|
+
return {
|
|
67
|
+
defaultLocalName: undefined,
|
|
68
|
+
namespaceLocalName: namespace[1],
|
|
69
|
+
specifiers: []
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const named = clause.match(/\{([\s\S]*)\}/);
|
|
74
|
+
if (!named) {
|
|
75
|
+
if (!identifierRegExp.test(clause)) return undefined;
|
|
76
|
+
return {
|
|
77
|
+
defaultLocalName: clause,
|
|
78
|
+
namespaceLocalName: undefined,
|
|
79
|
+
specifiers: []
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const beforeNamed = clause.slice(0, named.index).trim();
|
|
84
|
+
const afterNamed = clause.slice(named.index + named[0].length).trim();
|
|
85
|
+
if (afterNamed) return undefined;
|
|
86
|
+
let defaultLocalName;
|
|
87
|
+
if (beforeNamed) {
|
|
88
|
+
const defaultText = beforeNamed.replace(/,$/, '').trim();
|
|
89
|
+
if (!identifierRegExp.test(defaultText)) return undefined;
|
|
90
|
+
defaultLocalName = defaultText;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const specifiers = splitCommaList(named[1]).map((part) => parseImportSpecifier(part, options)).filter(Boolean);
|
|
94
|
+
if (splitCommaList(named[1]).filter((part) => part.trim()).length !== specifiers.length) return undefined;
|
|
95
|
+
return {
|
|
96
|
+
defaultLocalName,
|
|
97
|
+
namespaceLocalName: undefined,
|
|
98
|
+
specifiers
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseImportSpecifier(raw, options) {
|
|
103
|
+
let text = String(raw ?? '').trim();
|
|
104
|
+
if (!text) return undefined;
|
|
105
|
+
let typeOnly = Boolean(options.typeOnly);
|
|
106
|
+
if (text.startsWith('type ')) {
|
|
107
|
+
typeOnly = true;
|
|
108
|
+
text = text.slice(5).trim();
|
|
109
|
+
}
|
|
110
|
+
const match = text.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
|
|
111
|
+
if (!match) return undefined;
|
|
112
|
+
const importedName = match[1];
|
|
113
|
+
const localName = match[2] ?? importedName;
|
|
114
|
+
return {
|
|
115
|
+
importedName,
|
|
116
|
+
localName,
|
|
117
|
+
typeOnly,
|
|
118
|
+
canonical: importSpecifierCanonical({ importedName, localName, typeOnly })
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseDeclarationInfo(text) {
|
|
123
|
+
const trimmed = text.trim();
|
|
124
|
+
const defaultFunction = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?(?:\s+([A-Za-z_$][\w$]*))?\b/);
|
|
125
|
+
if (defaultFunction) return { kind: 'declaration', names: ['default'], declarationKind: 'function', exported: true, defaultExport: true };
|
|
126
|
+
const defaultClass = trimmed.match(/^export\s+default\s+(?:abstract\s+)?class(?:\s+([A-Za-z_$][\w$]*))?\b/);
|
|
127
|
+
if (defaultClass) return { kind: 'declaration', names: ['default'], declarationKind: 'class', exported: true, defaultExport: true };
|
|
128
|
+
|
|
129
|
+
const namedExport = trimmed.match(/^export\s+(type\s+)?\{([\s\S]+)\}\s*;?$/);
|
|
130
|
+
if (namedExport) {
|
|
131
|
+
const names = splitCommaList(namedExport[2]).map((part) => parseExportSpecifierName(part)).filter(Boolean);
|
|
132
|
+
const expectedCount = splitCommaList(namedExport[2]).filter((part) => part.trim()).length;
|
|
133
|
+
if (names.length !== expectedCount || names.length === 0) return undefined;
|
|
134
|
+
return { kind: 'export', names, declarationKind: 'export-list', exported: true, typeOnly: Boolean(namedExport[1]) };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const source = trimmed
|
|
138
|
+
.replace(/^export\s+/, '')
|
|
139
|
+
.replace(/^declare\s+/, '');
|
|
140
|
+
const functionMatch = source.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\b/);
|
|
141
|
+
if (functionMatch) return { kind: 'declaration', names: [functionMatch[1]], declarationKind: 'function', exported: trimmed.startsWith('export ') };
|
|
142
|
+
const classMatch = source.match(/^(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)\b/);
|
|
143
|
+
if (classMatch) return { kind: 'declaration', names: [classMatch[1]], declarationKind: 'class', exported: trimmed.startsWith('export ') };
|
|
144
|
+
const interfaceMatch = source.match(/^interface\s+([A-Za-z_$][\w$]*)\b/);
|
|
145
|
+
if (interfaceMatch) return { kind: 'declaration', names: [interfaceMatch[1]], declarationKind: 'interface', exported: trimmed.startsWith('export ') };
|
|
146
|
+
const typeMatch = source.match(/^type\s+([A-Za-z_$][\w$]*)\b/);
|
|
147
|
+
if (typeMatch) return { kind: 'declaration', names: [typeMatch[1]], declarationKind: 'type', exported: trimmed.startsWith('export ') };
|
|
148
|
+
const enumMatch = source.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)\b/);
|
|
149
|
+
if (enumMatch) return { kind: 'declaration', names: [enumMatch[1]], declarationKind: 'enum', exported: trimmed.startsWith('export ') };
|
|
150
|
+
const moduleMatch = source.match(/^(?:namespace|module)\s+([A-Za-z_$][\w$.]*)\b/);
|
|
151
|
+
if (moduleMatch) return { kind: 'declaration', names: [moduleMatch[1]], declarationKind: 'module', exported: trimmed.startsWith('export ') };
|
|
152
|
+
const variableMatch = source.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/);
|
|
153
|
+
if (variableMatch) return { kind: 'declaration', names: [variableMatch[1]], declarationKind: 'variable', exported: trimmed.startsWith('export ') };
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function unsupportedDeclarationPolicy(text, declarationInfo) {
|
|
158
|
+
const trimmed = text.trim();
|
|
159
|
+
if (/^\s*@/m.test(text)) {
|
|
160
|
+
return {
|
|
161
|
+
code: JsTsSafeMergeConflictCodes.unsupportedDecorator,
|
|
162
|
+
details: { reasonCode: 'unsupported-decorator-merge-anchor', declarationKind: declarationInfo.declarationKind }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (declarationInfo.declarationKind === 'function' && !trimmed.includes('{') && /;\s*$/.test(trimmed)) {
|
|
166
|
+
return {
|
|
167
|
+
code: JsTsSafeMergeConflictCodes.unsupportedOverload,
|
|
168
|
+
details: { reasonCode: 'unsupported-overload-merge-anchor', names: declarationInfo.names }
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (hasComputedMemberKey(text)) {
|
|
172
|
+
return {
|
|
173
|
+
code: JsTsSafeMergeConflictCodes.computedKey,
|
|
174
|
+
details: { reasonCode: 'computed-key', declarationKind: declarationInfo.declarationKind, names: declarationInfo.names }
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function hasComputedMemberKey(text) {
|
|
181
|
+
return /(?:^|[\n{,;])\s*(?:readonly\s+)?\[[^\]\n]+\]\??\s*(?::|\()/m.test(text);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function parseExportSpecifierName(raw) {
|
|
185
|
+
let text = String(raw ?? '').trim();
|
|
186
|
+
if (!text) return undefined;
|
|
187
|
+
if (text.startsWith('type ')) text = text.slice(5).trim();
|
|
188
|
+
const match = text.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
|
|
189
|
+
if (!match) return undefined;
|
|
190
|
+
return match[2] ?? match[1];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function splitCommaList(raw) {
|
|
194
|
+
return String(raw ?? '').split(',');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function importLedgerKey(importInfo) {
|
|
198
|
+
if (importInfo.sideEffectOnly) return `import:side-effect:${importInfo.moduleSpecifier}`;
|
|
199
|
+
return [
|
|
200
|
+
'import:named',
|
|
201
|
+
importInfo.moduleSpecifier,
|
|
202
|
+
importInfo.typeOnly ? 'type' : 'value',
|
|
203
|
+
importInfo.defaultLocalName ?? '',
|
|
204
|
+
importInfo.namespaceLocalName ?? ''
|
|
205
|
+
].join(':');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function importSpecifierCanonical(specifier) {
|
|
209
|
+
return `${specifier.typeOnly ? 'type ' : ''}${specifier.importedName}${specifier.localName === specifier.importedName ? '' : ` as ${specifier.localName}`}`;
|
|
210
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
export function skipTopLevelTrivia(sourceText, offset) {
|
|
2
|
+
let index = offset;
|
|
3
|
+
while (index < sourceText.length) {
|
|
4
|
+
const char = sourceText[index];
|
|
5
|
+
const next = sourceText[index + 1];
|
|
6
|
+
if (/\s/.test(char)) {
|
|
7
|
+
index += 1;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (char === '/' && next === '/') {
|
|
11
|
+
index = consumeLineComment(sourceText, index + 2);
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (char === '/' && next === '*') {
|
|
15
|
+
const end = sourceText.indexOf('*/', index + 2);
|
|
16
|
+
if (end === -1) return { offset: index, error: 'unterminated-block-comment' };
|
|
17
|
+
index = end + 2;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
return { offset: index };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function consumeLineComment(sourceText, offset) {
|
|
26
|
+
const lineEnd = sourceText.indexOf('\n', offset);
|
|
27
|
+
return lineEnd === -1 ? sourceText.length : lineEnd + 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function findStatementEnd(sourceText, start) {
|
|
31
|
+
const text = sourceText.slice(start);
|
|
32
|
+
const endOnBalancedBlock = statementCanEndOnBalancedBlock(text);
|
|
33
|
+
let braceDepth = 0;
|
|
34
|
+
let parenDepth = 0;
|
|
35
|
+
let bracketDepth = 0;
|
|
36
|
+
let openedBrace = false;
|
|
37
|
+
let quote;
|
|
38
|
+
let inTemplate = false;
|
|
39
|
+
let inLineComment = false;
|
|
40
|
+
let inBlockComment = false;
|
|
41
|
+
let escaped = false;
|
|
42
|
+
|
|
43
|
+
for (let index = start; index < sourceText.length; index += 1) {
|
|
44
|
+
const char = sourceText[index];
|
|
45
|
+
const next = sourceText[index + 1];
|
|
46
|
+
|
|
47
|
+
if (inLineComment) {
|
|
48
|
+
if (char === '\n') inLineComment = false;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (inBlockComment) {
|
|
52
|
+
if (char === '*' && next === '/') {
|
|
53
|
+
inBlockComment = false;
|
|
54
|
+
index += 1;
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (quote) {
|
|
59
|
+
if (escaped) {
|
|
60
|
+
escaped = false;
|
|
61
|
+
} else if (char === '\\') {
|
|
62
|
+
escaped = true;
|
|
63
|
+
} else if (char === quote) {
|
|
64
|
+
quote = undefined;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (inTemplate) {
|
|
69
|
+
if (escaped) {
|
|
70
|
+
escaped = false;
|
|
71
|
+
} else if (char === '\\') {
|
|
72
|
+
escaped = true;
|
|
73
|
+
} else if (char === '`') {
|
|
74
|
+
inTemplate = false;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (char === '/' && next === '/') {
|
|
80
|
+
inLineComment = true;
|
|
81
|
+
index += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (char === '/' && next === '*') {
|
|
85
|
+
inBlockComment = true;
|
|
86
|
+
index += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (char === '"' || char === "'") {
|
|
90
|
+
quote = char;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (char === '`') {
|
|
94
|
+
inTemplate = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === '(') {
|
|
98
|
+
parenDepth += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === ')') {
|
|
102
|
+
parenDepth -= 1;
|
|
103
|
+
if (parenDepth < 0) return { error: 'unbalanced-paren' };
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (char === '[') {
|
|
107
|
+
bracketDepth += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (char === ']') {
|
|
111
|
+
bracketDepth -= 1;
|
|
112
|
+
if (bracketDepth < 0) return { error: 'unbalanced-bracket' };
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (char === '{') {
|
|
116
|
+
braceDepth += 1;
|
|
117
|
+
openedBrace = true;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (char === '}') {
|
|
121
|
+
braceDepth -= 1;
|
|
122
|
+
if (braceDepth < 0) return { error: 'unbalanced-brace' };
|
|
123
|
+
if (endOnBalancedBlock && openedBrace && braceDepth === 0 && parenDepth === 0 && bracketDepth === 0) {
|
|
124
|
+
return { offset: consumeOptionalSameLineSemicolon(sourceText, index + 1) };
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (char === ';' && braceDepth === 0 && parenDepth === 0 && bracketDepth === 0) {
|
|
129
|
+
return { offset: index + 1 };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (quote || inTemplate || inBlockComment || braceDepth !== 0 || parenDepth !== 0 || bracketDepth !== 0) {
|
|
134
|
+
return { error: 'unterminated-statement' };
|
|
135
|
+
}
|
|
136
|
+
return { error: 'missing-top-level-statement-terminator' };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function consumeOptionalSameLineSemicolon(sourceText, offset) {
|
|
140
|
+
let index = offset;
|
|
141
|
+
while (index < sourceText.length && (sourceText[index] === ' ' || sourceText[index] === '\t')) index += 1;
|
|
142
|
+
if (sourceText[index] === ';') return index + 1;
|
|
143
|
+
return offset;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function statementCanEndOnBalancedBlock(text) {
|
|
147
|
+
const trimmed = text.trimStart();
|
|
148
|
+
return new RegExp(`^(?:export\\s+)?(?:declare\\s+)?(?:async\\s+)?function\\*?\\b`).test(trimmed)
|
|
149
|
+
|| new RegExp(`^(?:export\\s+)?(?:declare\\s+)?(?:abstract\\s+)?class\\b`).test(trimmed)
|
|
150
|
+
|| new RegExp(`^(?:export\\s+)?(?:declare\\s+)?interface\\b`).test(trimmed)
|
|
151
|
+
|| new RegExp(`^(?:export\\s+)?(?:declare\\s+)?(?:const\\s+)?enum\\b`).test(trimmed)
|
|
152
|
+
|| new RegExp(`^(?:export\\s+)?(?:declare\\s+)?(?:namespace|module)\\b`).test(trimmed)
|
|
153
|
+
|| new RegExp(`^export\\s+default\\s+(?:async\\s+)?function\\*?\\b`).test(trimmed)
|
|
154
|
+
|| new RegExp(`^export\\s+default\\s+(?:abstract\\s+)?class\\b`).test(trimmed);
|
|
155
|
+
}
|