@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,64 @@
|
|
|
1
|
+
function canonicalizeSourceBodies(sourceText, preparedRegions, side) {
|
|
2
|
+
let output = sourceText;
|
|
3
|
+
const replacements = preparedRegions
|
|
4
|
+
.map((region) => ({ range: region[side], replacement: region.base.body }))
|
|
5
|
+
.sort((left, right) => right.range.bodyStart - left.range.bodyStart);
|
|
6
|
+
for (const { range, replacement } of replacements) {
|
|
7
|
+
output = `${output.slice(0, range.bodyStart)}${replacement}${output.slice(range.bodyEnd)}`;
|
|
8
|
+
}
|
|
9
|
+
return output;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function applyMemberAdditions(headSourceText, preparedRegions) {
|
|
13
|
+
let output = headSourceText;
|
|
14
|
+
const replacements = preparedRegions
|
|
15
|
+
.filter((region) => region.workerAddedMembers.length)
|
|
16
|
+
.map((region) => ({ range: region.head, replacement: appendMembersToBody(region.head.body, region.workerAddedMembers) }))
|
|
17
|
+
.sort((left, right) => right.range.bodyStart - left.range.bodyStart);
|
|
18
|
+
for (const { range, replacement } of replacements) {
|
|
19
|
+
output = `${output.slice(0, range.bodyStart)}${replacement}${output.slice(range.bodyEnd)}`;
|
|
20
|
+
}
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function appendMembersToBody(body, members) {
|
|
25
|
+
if (!members.length) return body;
|
|
26
|
+
const indent = inferMemberIndent(body) ?? inferMemberIndent(members.map((member) => member.text).join('\n')) ?? ' ';
|
|
27
|
+
const addedText = members.map((member) => normalizeMemberForInsertion(member.text, indent)).join('\n');
|
|
28
|
+
const trailing = body.match(/\s*$/)?.[0] ?? '';
|
|
29
|
+
const before = body.slice(0, body.length - trailing.length);
|
|
30
|
+
if (!before.trim()) {
|
|
31
|
+
const closingIndent = trailing.includes('\n') ? trailing.slice(trailing.lastIndexOf('\n') + 1) : '';
|
|
32
|
+
return `\n${addedText}\n${closingIndent}`;
|
|
33
|
+
}
|
|
34
|
+
return `${before}${before.endsWith('\n') ? '' : '\n'}${addedText}${trailing.includes('\n') ? trailing : `\n${trailing}`}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function inferMemberIndent(text) {
|
|
38
|
+
return String(text ?? '').match(/\n([ \t]*)\S/)?.[1];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeMemberForInsertion(text, indent) {
|
|
42
|
+
const lines = String(text ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
43
|
+
while (lines.length && !lines[0].trim()) lines.shift();
|
|
44
|
+
while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
|
|
45
|
+
const commonIndent = minimumIndent(lines);
|
|
46
|
+
return lines.map((line) => {
|
|
47
|
+
const normalized = commonIndent ? line.slice(Math.min(commonIndent, leadingWhitespace(line))) : line;
|
|
48
|
+
return normalized.trim() ? `${indent}${normalized}` : normalized;
|
|
49
|
+
}).join('\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function minimumIndent(lines) {
|
|
53
|
+
const indents = lines.filter((line) => line.trim()).map(leadingWhitespace);
|
|
54
|
+
return indents.length ? Math.min(...indents) : 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function leadingWhitespace(line) {
|
|
58
|
+
return line.match(/^[ \t]*/)?.[0].length ?? 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
applyMemberAdditions,
|
|
63
|
+
canonicalizeSourceBodies
|
|
64
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function normalizeMemberText(text) {
|
|
2
|
+
return String(text ?? '')
|
|
3
|
+
.replace(/\r\n/g, '\n')
|
|
4
|
+
.replace(/\r/g, '\n')
|
|
5
|
+
.split('\n')
|
|
6
|
+
.map((line) => line.trimEnd())
|
|
7
|
+
.join('\n')
|
|
8
|
+
.trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function uniqueStrings(values) {
|
|
12
|
+
return [...new Set(values.filter(Boolean).map(String))];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
normalizeMemberText,
|
|
17
|
+
uniqueStrings
|
|
18
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
findContainer,
|
|
3
|
+
normalizeKind
|
|
4
|
+
} from './js-ts-semantic-merge-member-containers.js';
|
|
5
|
+
export {
|
|
6
|
+
parseMembers
|
|
7
|
+
} from './js-ts-semantic-merge-member-segments.js';
|
|
8
|
+
export {
|
|
9
|
+
applyMemberAdditions,
|
|
10
|
+
canonicalizeSourceBodies
|
|
11
|
+
} from './js-ts-semantic-merge-member-source.js';
|
|
12
|
+
export {
|
|
13
|
+
normalizeMemberText,
|
|
14
|
+
uniqueStrings
|
|
15
|
+
} from './js-ts-semantic-merge-member-utils.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
JsTsSafeMergeConflictCodes,
|
|
3
|
+
JsTsSafeMergeGateIds,
|
|
4
|
+
JsTsSafeMergeStatuses,
|
|
5
|
+
safeMergeJsTsImportsAndDeclarations
|
|
6
|
+
} from './js-ts-safe-merge.js';
|
|
7
|
+
export {
|
|
8
|
+
mergeJsTsSafeMemberAdditions,
|
|
9
|
+
safeMergeJsTsMembers
|
|
10
|
+
} from './js-ts-safe-member-merge.js';
|
|
11
|
+
export {
|
|
12
|
+
createJsTsSemanticMergeConflictExplanation,
|
|
13
|
+
createJsTsSemanticMergeGateResult,
|
|
14
|
+
JsTsSemanticMergeConflictClasses,
|
|
15
|
+
JsTsSemanticMergeGateStatuses
|
|
16
|
+
} from './js-ts-semantic-merge-contracts.js';
|
|
17
|
+
export {
|
|
18
|
+
createJsTsSemanticConflictSidecars,
|
|
19
|
+
JsTsSemanticConflictSidecarClasses,
|
|
20
|
+
summarizeJsTsSemanticConflictSidecars
|
|
21
|
+
} from './js-ts-semantic-conflict-sidecars.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function lightweightEffectKinds(line) {
|
|
2
|
+
const kinds = [];
|
|
3
|
+
if (/\bawait\b|import\s*\(/.test(line)) kinds.push('async');
|
|
4
|
+
if (hasGlobalNetworkCall(line)) kinds.push('network');
|
|
5
|
+
if (/\b(localStorage|sessionStorage|indexedDB|caches|cookie)\b/.test(line)) kinds.push('storage');
|
|
6
|
+
if (hasGlobalSchedulerCall(line)) kinds.push('scheduler');
|
|
7
|
+
if (/\b(console|process|Deno|Bun)\s*\./.test(line)) kinds.push('host');
|
|
8
|
+
if (hasBrowserEffect(line)) kinds.push('browser');
|
|
9
|
+
return kinds;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hasGlobalNetworkCall(line) {
|
|
13
|
+
return hasBareCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'])
|
|
14
|
+
|| hasGlobalPropertyCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource']);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function hasGlobalSchedulerCall(line) {
|
|
18
|
+
const names = [
|
|
19
|
+
'setTimeout',
|
|
20
|
+
'setInterval',
|
|
21
|
+
'clearTimeout',
|
|
22
|
+
'clearInterval',
|
|
23
|
+
'requestAnimationFrame',
|
|
24
|
+
'cancelAnimationFrame',
|
|
25
|
+
'requestIdleCallback',
|
|
26
|
+
'cancelIdleCallback',
|
|
27
|
+
'queueMicrotask',
|
|
28
|
+
'setImmediate',
|
|
29
|
+
'clearImmediate'
|
|
30
|
+
];
|
|
31
|
+
return hasBareCall(line, names) || hasGlobalPropertyCall(line, names);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hasBrowserEffect(line) {
|
|
35
|
+
return /\b(document|window|navigator|location|history)\s*\./.test(line)
|
|
36
|
+
|| hasGlobalConstructorCall(line, ['Worker', 'SharedWorker']);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasBareCall(line, names) {
|
|
40
|
+
return names.some((name) => new RegExp(`(?:^|[^\\w$.])${name}\\s*\\(`).test(line));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hasGlobalPropertyCall(line, names) {
|
|
44
|
+
return names.some((name) => new RegExp(`\\b(?:window|globalThis|self)\\s*\\.\\s*${name}\\s*\\(`).test(line));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasGlobalConstructorCall(line, names) {
|
|
48
|
+
return names.some((name) => new RegExp(`(?:^|[^\\w$.])new\\s+(?:(?:window|globalThis|self)\\s*\\.\\s*)?${name}\\s*\\(`).test(line));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { lightweightEffectKinds };
|
|
@@ -41,7 +41,7 @@ export function isDependencyIdentifier(value) {
|
|
|
41
41
|
export function dependencyScanRanges(input, declarations, lines) {
|
|
42
42
|
const ordered = [...(declarations ?? [])].sort((left, right) => (left.span?.startLine ?? 0) - (right.span?.startLine ?? 0));
|
|
43
43
|
return ordered
|
|
44
|
-
.filter((declaration) => declaration?.symbolId && declaration.role !== 'import')
|
|
44
|
+
.filter((declaration) => declaration?.symbolId && declaration.role !== 'import' && shouldScanDependencyDeclaration(input, declaration))
|
|
45
45
|
.map((declaration, index) => ({
|
|
46
46
|
declaration,
|
|
47
47
|
startLine: declaration.span?.startLine ?? 1,
|
|
@@ -49,6 +49,17 @@ export function dependencyScanRanges(input, declarations, lines) {
|
|
|
49
49
|
}));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function shouldScanDependencyDeclaration(input, declaration) {
|
|
53
|
+
if (!isJavaScriptLikeLanguage(input)) return true;
|
|
54
|
+
if (declaration.metadata?.signatureOnly || declaration.fields?.typeKind) return false;
|
|
55
|
+
if (/^Type(?:Alias|Method|Property|FunctionProperty)/.test(String(declaration?.kind ?? ''))) return false;
|
|
56
|
+
return !['interface', 'type'].includes(String(declaration?.symbolKind ?? '').toLowerCase());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isJavaScriptLikeLanguage(input) {
|
|
60
|
+
return ['javascript', 'typescript'].includes(String(input?.language ?? '').toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
export function maskDependencyLine(input, line, state) {
|
|
53
64
|
const language = String(input.language ?? '').toLowerCase();
|
|
54
65
|
const text = String(line ?? '');
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
isDependencyIdentifier,
|
|
8
8
|
maskDependencyLine
|
|
9
9
|
} from './lightweight-dependency-language.js';
|
|
10
|
+
import { lightweightEffectKinds } from './lightweight-dependency-effects.js';
|
|
10
11
|
import { sourceLines } from './native-region-scanner-core.js';
|
|
11
12
|
|
|
12
13
|
export function lightweightDependencyRelations(input, declarations, documentId) {
|
|
@@ -108,6 +109,7 @@ function addLightweightSemanticFacts(input, documentId, declaration, line, lineN
|
|
|
108
109
|
function shouldScanRuntimeFacts(input, declaration) {
|
|
109
110
|
if (!isJavaScriptLike(input)) return true;
|
|
110
111
|
if (declaration?.fields?.typeKind) return false;
|
|
112
|
+
if (['object', 'array'].includes(declaration?.fields?.initializerKind)) return false;
|
|
111
113
|
if (/^Type(?:Alias|Method|Property|FunctionProperty)/.test(String(declaration?.kind ?? ''))) return false;
|
|
112
114
|
return !['interface', 'type'].includes(String(declaration?.symbolKind ?? '').toLowerCase());
|
|
113
115
|
}
|
|
@@ -119,6 +121,10 @@ function isJavaScriptLike(input) {
|
|
|
119
121
|
function isIgnoredDependencyOccurrence(input, line, startIndex, name) {
|
|
120
122
|
if (!isJavaScriptLike(input)) return false;
|
|
121
123
|
const endIndex = startIndex + String(name).length;
|
|
124
|
+
const beforeText = line.slice(0, startIndex);
|
|
125
|
+
if (/\b(?:async\s+)?function\*?\s*$/.test(beforeText) || /\bclass\s*$/.test(beforeText)) return true;
|
|
126
|
+
const afterText = line.slice(endIndex);
|
|
127
|
+
if (/^\s*\([^)]*\)\s*\{/.test(afterText) && /^\s*(?:async\s+)?(?:get\s+|set\s+)?$/.test(beforeText)) return true;
|
|
122
128
|
const previous = previousNonSpace(line, startIndex - 1);
|
|
123
129
|
const next = nextNonSpace(line, endIndex);
|
|
124
130
|
return previous === '.' || next === ':';
|
|
@@ -148,17 +154,6 @@ function lightweightControlFlowKinds(line, state = {}) {
|
|
|
148
154
|
return kinds;
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
function lightweightEffectKinds(line) {
|
|
152
|
-
const kinds = [];
|
|
153
|
-
if (/\bawait\b|import\s*\(/.test(line)) kinds.push('async');
|
|
154
|
-
if (hasGlobalNetworkCall(line)) kinds.push('network');
|
|
155
|
-
if (/\b(localStorage|sessionStorage|indexedDB|caches|cookie)\b/.test(line)) kinds.push('storage');
|
|
156
|
-
if (/\b(setTimeout|setInterval|requestAnimationFrame|queueMicrotask)\s*\(/.test(line)) kinds.push('scheduler');
|
|
157
|
-
if (/\b(console|process|Deno|Bun)\s*\./.test(line)) kinds.push('host');
|
|
158
|
-
if (/\b(document|window|navigator|location|history)\s*\./.test(line)) kinds.push('browser');
|
|
159
|
-
return kinds;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
157
|
function lightweightMutationKinds(line) {
|
|
163
158
|
const kinds = [];
|
|
164
159
|
if (/\bdelete\s+[A-Za-z_$][\w$.[\]]*/.test(line)) kinds.push('delete');
|
|
@@ -199,24 +194,11 @@ function blockBraceDelta(line) {
|
|
|
199
194
|
return delta;
|
|
200
195
|
}
|
|
201
196
|
|
|
202
|
-
function hasGlobalNetworkCall(line) {
|
|
203
|
-
return hasBareCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'])
|
|
204
|
-
|| hasGlobalPropertyCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource']);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function hasBareCall(line, names) {
|
|
208
|
-
return names.some((name) => new RegExp(`(?:^|[^\\w$.])${name}\\s*\\(`).test(line));
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function hasGlobalPropertyCall(line, names) {
|
|
212
|
-
return names.some((name) => new RegExp(`\\b(?:window|globalThis|self)\\s*\\.\\s*${name}\\s*\\(`).test(line));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
197
|
function hasRuntimeAssignment(line) {
|
|
216
198
|
const text = String(line ?? '');
|
|
217
199
|
for (let index = 0; index < text.length; index += 1) {
|
|
218
200
|
if (text[index] !== '=' || !isPlainAssignmentOperator(text, index)) continue;
|
|
219
|
-
if (!isLocalDeclarationInitializer(text, index)) return true;
|
|
201
|
+
if (!isLocalDeclarationInitializer(text, index) && !isJsxAttributeInitializer(text, index)) return true;
|
|
220
202
|
}
|
|
221
203
|
return false;
|
|
222
204
|
}
|
|
@@ -237,6 +219,8 @@ function isLocalDeclarationInitializer(text, index) {
|
|
|
237
219
|
|| /^(?:export\s+)?type\s+[A-Za-z_$][\w$]*(?:\s*<[^>]+>)?\s*$/.test(statement);
|
|
238
220
|
}
|
|
239
221
|
|
|
222
|
+
function isJsxAttributeInitializer(text, index) { const before = text.slice(0, index); const open = before.lastIndexOf('<'); return open >= 0 && before.lastIndexOf('>') < open && /<[A-Za-z][^<>{}]*\s[A-Za-z_$][\w:.-]*\s*$/.test(before.slice(open)); }
|
|
223
|
+
|
|
240
224
|
function addFactRecord(input, documentId, declaration, predicate, factKind, lineNumber, records) {
|
|
241
225
|
const key = `${declaration.symbolId}|${predicate}|${factKind}|${lineNumber}`;
|
|
242
226
|
if (records.seen.has(key)) return;
|
|
@@ -280,7 +264,9 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
|
|
|
280
264
|
confidence: 'lexical-reference',
|
|
281
265
|
sourceDocumentId: documentId,
|
|
282
266
|
sourceName: caller.name,
|
|
283
|
-
targetName: target.name
|
|
267
|
+
targetName: target.name,
|
|
268
|
+
occurrenceId,
|
|
269
|
+
sourceSpan: span
|
|
284
270
|
}
|
|
285
271
|
});
|
|
286
272
|
records.occurrences.push({
|
|
@@ -306,5 +292,6 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
|
|
|
306
292
|
}
|
|
307
293
|
|
|
308
294
|
function isCallReference(line, afterIdentifierIndex) {
|
|
309
|
-
|
|
295
|
+
const rest = String(line ?? '').slice(afterIdentifierIndex);
|
|
296
|
+
return /^\s*(?:\(|\?\.\s*\(|\?\.\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\])(?:\s*(?:\.|\?\.)\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\]))*\s*(?:\?\.)?\s*\()/.test(rest);
|
|
310
297
|
}
|
|
@@ -8,7 +8,7 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
|
|
|
8
8
|
languageKind: `${input.language}.${languageKind}`,
|
|
9
9
|
name,
|
|
10
10
|
symbolKind,
|
|
11
|
-
symbolId: `symbol:${input.language}:${idFragment(name)}`,
|
|
11
|
+
symbolId: options.symbolId ?? `symbol:${input.language}:${idFragment(name)}`,
|
|
12
12
|
span: options.span ?? spanForLine(input, lineNumber),
|
|
13
13
|
fields,
|
|
14
14
|
metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
|
|
@@ -18,6 +18,22 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function nativeSignatureDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, _hasBody = false, options = {}) {
|
|
22
|
+
const signatureParts = [name, fields.signature, fields.returnType, fields.valueType, ...(fields.parameters ?? [])];
|
|
23
|
+
const signatureKey = idFragment(signatureParts.filter(Boolean).join(':') || `${name}:${lineNumber}`);
|
|
24
|
+
const sourcePath = input.sourcePath ?? `${input.language}:memory`;
|
|
25
|
+
const regionKind = options.regionKind ?? 'declaration';
|
|
26
|
+
return nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields, false, {
|
|
27
|
+
regionKind,
|
|
28
|
+
symbolId: options.symbolId ?? `symbol:${input.language}:signature:${idFragment(name)}:${signatureKey}`,
|
|
29
|
+
metadata: {
|
|
30
|
+
signatureOnly: true,
|
|
31
|
+
ownershipRegionKey: options.ownershipRegionKey ?? `source#${sourcePath}#${regionKind}#${name}#${signatureKey}`,
|
|
32
|
+
...options.metadata
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind, options = {}) {
|
|
22
38
|
const name = String(options.name ?? importPath);
|
|
23
39
|
const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
|
|
@@ -65,6 +81,20 @@ function nativeImportBindingDeclaration(input, lineNumber, importPath, binding,
|
|
|
65
81
|
});
|
|
66
82
|
}
|
|
67
83
|
|
|
84
|
+
function nativeExportDeclaration(input, lineNumber, exportedName, languageKind = 'ExportDeclaration', fields = {}, options = {}) {
|
|
85
|
+
const name = String(options.name ?? exportedName ?? 'default');
|
|
86
|
+
return nativeDeclaration(input, lineNumber, languageKind, options.symbolKind ?? 'export', name, {
|
|
87
|
+
exportedName: String(exportedName ?? name),
|
|
88
|
+
...fields
|
|
89
|
+
}, false, {
|
|
90
|
+
role: options.role ?? 'export',
|
|
91
|
+
regionKind: options.regionKind ?? 'export',
|
|
92
|
+
span: options.span,
|
|
93
|
+
symbolId: options.symbolId ?? `symbol:${input.language}:export:${idFragment(name)}`,
|
|
94
|
+
metadata: { scan: 'lightweight-export', ...options.metadata }
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
function nativeMacroLoss(input, lineNumber, source, kind, name = idFragment(source).slice(0, 40)) {
|
|
69
99
|
const nodeId = `native_${kind}_${lineNumber}_${idFragment(name)}`;
|
|
70
100
|
return {
|
|
@@ -202,9 +232,11 @@ export {
|
|
|
202
232
|
jsControlKeyword,
|
|
203
233
|
lightweightCoverageLosses,
|
|
204
234
|
nativeDeclaration,
|
|
235
|
+
nativeExportDeclaration,
|
|
205
236
|
nativeImportDeclaration,
|
|
206
237
|
nativeImportBindingDeclaration,
|
|
207
238
|
nativeMacroLoss,
|
|
239
|
+
nativeSignatureDeclaration,
|
|
208
240
|
sourceLines,
|
|
209
241
|
splitParameters,
|
|
210
242
|
splitTypeParameters
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { uniqueStrings, upperFirst } from './native-import-utils.js';
|
|
2
|
+
import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
|
|
3
|
+
import { braceBlockSpan } from './native-region-scanner-spans.js';
|
|
4
|
+
|
|
5
|
+
function scanCSharp(input) {
|
|
6
|
+
const declarations = [];
|
|
7
|
+
const lines = sourceLines(input.sourceText);
|
|
8
|
+
const blockStack = [];
|
|
9
|
+
let braceDepth = 0;
|
|
10
|
+
for (const [index, { line, number }] of lines.entries()) {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
|
|
13
|
+
while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
|
|
14
|
+
|
|
15
|
+
let match;
|
|
16
|
+
if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
|
|
17
|
+
declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
|
|
18
|
+
} else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
|
|
19
|
+
declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
|
|
20
|
+
} else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
|
|
21
|
+
const hasBody = trimmed.includes('{');
|
|
22
|
+
declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, hasBody, spanOptions(input, lines, index, hasBody)));
|
|
23
|
+
if (hasBody) blockStack.push({ kind: 'namespace', name: match[1], bodyDepth: braceDepth + 1 });
|
|
24
|
+
} else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|unsafe|new)\s+)*)delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
|
|
25
|
+
declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[3], {
|
|
26
|
+
returnType: match[2].trim(),
|
|
27
|
+
parameters: splitParameters(match[4]),
|
|
28
|
+
modifiers: csharpModifiers(match[1])
|
|
29
|
+
}, false));
|
|
30
|
+
} else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly|ref|unsafe)\s+)*)(class|interface|struct|enum|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/))) {
|
|
31
|
+
const owner = nearestType(blockStack);
|
|
32
|
+
const name = owner ? `${owner.name}.${match[3]}` : match[3];
|
|
33
|
+
const hasBody = trimmed.includes('{');
|
|
34
|
+
declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[2]), csharpSymbolKind(match[2]), name, {
|
|
35
|
+
csharpKind: match[2].replace(/\s+/g, ' '),
|
|
36
|
+
modifiers: csharpModifiers(match[1]),
|
|
37
|
+
...(owner ? { owner: owner.name } : {})
|
|
38
|
+
}, hasBody, spanOptions(input, lines, index, hasBody)));
|
|
39
|
+
if (hasBody) blockStack.push({ kind: csharpTypeStackKind(match[2]), name, bodyDepth: braceDepth + 1 });
|
|
40
|
+
} else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern|new|unsafe|readonly)\s+)*)(?:[A-Za-z_][\w<>\[\].?,\s]*\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:=>.*|\{.*|;)?$/))) {
|
|
41
|
+
const parameters = splitParameters(match[3]);
|
|
42
|
+
const owner = nearestType(blockStack);
|
|
43
|
+
const extensionReceiver = csharpExtensionReceiver(parameters);
|
|
44
|
+
const target = csharpMethodTarget(owner, match[1], match[2], extensionReceiver);
|
|
45
|
+
const hasBody = trimmed.includes('{') || trimmed.includes('=>');
|
|
46
|
+
declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', target.owner ? 'method' : 'function', target.name, {
|
|
47
|
+
parameters,
|
|
48
|
+
methodName: match[2],
|
|
49
|
+
modifiers: csharpModifiers(match[1]),
|
|
50
|
+
...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
|
|
51
|
+
...(extensionReceiver ? { extensionReceiver } : {})
|
|
52
|
+
}, hasBody, {
|
|
53
|
+
...spanOptions(input, lines, index, trimmed.includes('{')),
|
|
54
|
+
metadata: {
|
|
55
|
+
methodName: match[2],
|
|
56
|
+
modifiers: csharpModifiers(match[1]),
|
|
57
|
+
...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
|
|
58
|
+
...(extensionReceiver ? { extensionReceiver } : {})
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
if (trimmed.includes('{')) blockStack.push({ kind: 'method', name: target.name, bodyDepth: braceDepth + 1 });
|
|
62
|
+
} else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|unsafe)\s+)*)event\s+(.+?)\s+([A-Za-z_]\w*)\s*(?:[;{=]|=>)/))) {
|
|
63
|
+
const owner = nearestType(blockStack);
|
|
64
|
+
const name = owner ? `${owner.name}.${match[3]}` : match[3];
|
|
65
|
+
declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', name, {
|
|
66
|
+
eventType: match[2].trim(),
|
|
67
|
+
accessors: csharpAccessors(trimmed),
|
|
68
|
+
modifiers: csharpModifiers(match[1]),
|
|
69
|
+
...(owner ? { owner: owner.name, eventName: match[3] } : {})
|
|
70
|
+
}, trimmed.includes('{'), spanOptions(input, lines, index, trimmed.includes('{'))));
|
|
71
|
+
} else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|required|readonly|unsafe)\s+)*)([A-Za-z_][\w<>\[\].?,\s]*\??)\s+([A-Za-z_]\w*)\s*(?:\{|=>)/))) {
|
|
72
|
+
const owner = nearestType(blockStack);
|
|
73
|
+
const name = owner ? `${owner.name}.${match[3]}` : match[3];
|
|
74
|
+
declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', name, {
|
|
75
|
+
propertyType: match[2].trim(),
|
|
76
|
+
accessors: csharpAccessors(trimmed),
|
|
77
|
+
modifiers: csharpModifiers(match[1]),
|
|
78
|
+
...(owner ? { owner: owner.name, propertyName: match[3] } : {})
|
|
79
|
+
}, trimmed.includes('{') || trimmed.includes('=>'), spanOptions(input, lines, index, trimmed.includes('{'))));
|
|
80
|
+
}
|
|
81
|
+
braceDepth = Math.max(0, braceDepth + braceDelta(line));
|
|
82
|
+
}
|
|
83
|
+
return declarations;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function spanOptions(input, lines, index, hasBraceBody) {
|
|
87
|
+
return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function depthAfterLeadingClosers(trimmed, depth) {
|
|
91
|
+
const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
|
|
92
|
+
return Math.max(0, depth - closers);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function nearestType(blockStack) {
|
|
96
|
+
for (let index = blockStack.length - 1; index >= 0; index -= 1) {
|
|
97
|
+
if (blockStack[index].kind === 'method') return undefined;
|
|
98
|
+
if (['class', 'interface', 'struct', 'record'].includes(blockStack[index].kind)) return blockStack[index];
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function csharpMethodTarget(owner, modifiers, methodName, extensionReceiver) {
|
|
104
|
+
if (extensionReceiver) return { name: `${extensionReceiver.type}.extension.${methodName}`, owner: extensionReceiver.type, receiverKind: 'extension' };
|
|
105
|
+
if (!owner) return { name: methodName };
|
|
106
|
+
const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
|
|
107
|
+
return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function csharpModifiers(raw) {
|
|
111
|
+
return splitParameters(String(raw ?? '').trim().replace(/\s+/g, ','));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function csharpTypeStackKind(kind) {
|
|
115
|
+
const normalized = String(kind).replace(/\s+/g, ' ');
|
|
116
|
+
if (normalized.startsWith('record')) return 'record';
|
|
117
|
+
if (normalized === 'enum') return 'enum';
|
|
118
|
+
if (normalized === 'struct') return 'struct';
|
|
119
|
+
if (normalized === 'interface') return 'interface';
|
|
120
|
+
return 'class';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function csharpSymbolKind(kind) {
|
|
124
|
+
const normalized = String(kind).replace(/\s+/g, ' ');
|
|
125
|
+
if (normalized === 'interface') return 'interface';
|
|
126
|
+
if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
|
|
127
|
+
return 'class';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function csharpDeclarationKind(kind) {
|
|
131
|
+
const normalized = String(kind).replace(/\s+/g, ' ');
|
|
132
|
+
if (normalized === 'record struct') return 'RecordStructDeclaration';
|
|
133
|
+
if (normalized === 'record class') return 'RecordClassDeclaration';
|
|
134
|
+
if (normalized === 'record') return 'RecordDeclaration';
|
|
135
|
+
return `${upperFirst(normalized)}Declaration`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function csharpExtensionReceiver(parameters) {
|
|
139
|
+
const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
|
|
140
|
+
return match ? { type: match[1].trim(), name: match[2] } : undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function csharpAccessors(source) {
|
|
144
|
+
return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function braceDelta(source) {
|
|
148
|
+
return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { scanCSharp };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { upperFirst } from './native-import-utils.js';
|
|
2
|
+
import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
|
|
3
|
+
import { braceBlockSpan } from './native-region-scanner-spans.js';
|
|
4
|
+
|
|
5
|
+
function scanDart(input) {
|
|
6
|
+
const declarations = [];
|
|
7
|
+
const lines = sourceLines(input.sourceText);
|
|
8
|
+
const blockStack = [];
|
|
9
|
+
let braceDepth = 0;
|
|
10
|
+
for (const [index, { line, number }] of lines.entries()) {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
|
|
13
|
+
while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
|
|
14
|
+
|
|
15
|
+
let match;
|
|
16
|
+
if ((match = trimmed.match(/^(?:import|export)\s+['"]([^'"]+)['"]/))) {
|
|
17
|
+
declarations.push(nativeImportDeclaration(input, number, match[1], 'UriBasedDirective', 'library'));
|
|
18
|
+
} else if ((match = trimmed.match(/^part\s+['"]([^'"]+)['"]/))) {
|
|
19
|
+
declarations.push(nativeImportDeclaration(input, number, match[1], 'PartDirective', 'library'));
|
|
20
|
+
} else if ((match = trimmed.match(/^(?:(?:abstract|base|final|interface|sealed)\s+)*(class|mixin|enum)\s+([A-Za-z_]\w*)/))) {
|
|
21
|
+
const hasBody = trimmed.includes('{');
|
|
22
|
+
declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, dartSymbolKind(match[1]), match[2], {}, hasBody, spanOptions(input, lines, index, hasBody)));
|
|
23
|
+
if (hasBody) blockStack.push({ kind: match[1], name: match[2], bodyDepth: braceDepth + 1 });
|
|
24
|
+
} else if ((match = trimmed.match(/^extension(?:\s+([A-Za-z_]\w*))?\s+on\s+([A-Za-z_]\w*(?:<[^>]+>)?)\s*\{/))) {
|
|
25
|
+
const name = match[1] ? `${match[1]}.extension` : `${match[2]}.extension`;
|
|
26
|
+
declarations.push(nativeDeclaration(input, number, 'ExtensionDeclaration', 'implementation', name, {
|
|
27
|
+
receiverType: match[2],
|
|
28
|
+
...(match[1] ? { extensionName: match[1] } : {})
|
|
29
|
+
}, true, spanOptions(input, lines, index, true)));
|
|
30
|
+
blockStack.push({ kind: 'extension', name, receiverType: match[2], bodyDepth: braceDepth + 1 });
|
|
31
|
+
} else if ((match = trimmed.match(/^typedef\s+([A-Za-z_]\w*)\b/))) {
|
|
32
|
+
declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], {}, false));
|
|
33
|
+
} else if ((match = trimmed.match(/^((?:(?:external|static)\s+)*)(?:[A-Za-z_]\w*(?:<[^>]+>)?\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:async\s*)?(?:\{|=>|;)/))) {
|
|
34
|
+
const owner = nearestContainer(blockStack);
|
|
35
|
+
const target = dartFunctionTarget(owner, match[1], match[2]);
|
|
36
|
+
const hasBraceBody = trimmed.includes('{');
|
|
37
|
+
declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', target.owner ? 'method' : 'function', target.name, {
|
|
38
|
+
parameters: splitParameters(match[3]),
|
|
39
|
+
...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
|
|
40
|
+
...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
|
|
41
|
+
}, hasBraceBody || trimmed.includes('=>'), {
|
|
42
|
+
...spanOptions(input, lines, index, hasBraceBody),
|
|
43
|
+
metadata: {
|
|
44
|
+
...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
|
|
45
|
+
...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
if (hasBraceBody) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
|
|
49
|
+
} else if ((match = trimmed.match(/^(?:(?:static|external|late)\s+)*(?:const|final|var)\s+(?:[A-Za-z_]\w*(?:<[^>]+>)?\??\s+)?([A-Za-z_]\w*)\b/))) {
|
|
50
|
+
declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
|
|
51
|
+
}
|
|
52
|
+
braceDepth = Math.max(0, braceDepth + braceDelta(line));
|
|
53
|
+
}
|
|
54
|
+
return declarations;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function spanOptions(input, lines, index, hasBraceBody) {
|
|
58
|
+
return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function depthAfterLeadingClosers(trimmed, depth) {
|
|
62
|
+
const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
|
|
63
|
+
return Math.max(0, depth - closers);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function nearestContainer(blockStack) {
|
|
67
|
+
for (let index = blockStack.length - 1; index >= 0; index -= 1) {
|
|
68
|
+
if (blockStack[index].kind === 'function') return undefined;
|
|
69
|
+
if (['class', 'mixin', 'enum', 'extension'].includes(blockStack[index].kind)) return blockStack[index];
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function dartFunctionTarget(owner, modifiers, methodName) {
|
|
75
|
+
if (!owner) return { name: methodName };
|
|
76
|
+
if (owner.kind === 'extension') return { name: `${owner.name}.${methodName}`, owner: owner.name, receiverKind: 'extension' };
|
|
77
|
+
const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
|
|
78
|
+
return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function braceDelta(source) {
|
|
82
|
+
return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function dartSymbolKind(kind) {
|
|
86
|
+
if (kind === 'mixin') return 'trait';
|
|
87
|
+
if (kind === 'enum') return 'type';
|
|
88
|
+
return 'class';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { scanDart };
|