@shapeshift-labs/frontier-lang-compiler 0.2.102 → 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.
Files changed (134) hide show
  1. package/README.md +13 -0
  2. package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
  3. package/dist/declarations/bidirectional-target-change.d.ts +10 -0
  4. package/dist/declarations/js-ts-safe-member-merge.d.ts +58 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +120 -0
  6. package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
  7. package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
  8. package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
  9. package/dist/declarations/native-import-losses.d.ts +3 -0
  10. package/dist/declarations/native-project-admission-semantic-evidence.d.ts +34 -0
  11. package/dist/declarations/native-project-admission.d.ts +6 -10
  12. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  13. package/dist/declarations/semantic-edit-script.d.ts +10 -4
  14. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  15. package/dist/declarations/semantic-patch-bundle-overlaps.d.ts +1 -0
  16. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  17. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  18. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  19. package/dist/declarations/source-preservation.d.ts +72 -0
  20. package/dist/declarations/universal-capability.d.ts +4 -0
  21. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  22. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  23. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  24. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.js +3 -0
  27. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  28. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  29. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  30. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  31. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  32. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  33. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  34. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  35. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  36. package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +14 -2
  37. package/dist/internal/index-impl/diffNativeSymbols.js +82 -1
  38. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  39. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +1 -1
  40. package/dist/internal/index-impl/projectImportAdmissionSemanticWarnings.js +178 -0
  41. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +22 -3
  42. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +54 -69
  43. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  44. package/dist/internal/index-impl/replaySemanticEditProjection.js +78 -78
  45. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  46. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  47. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  48. package/dist/internal/index-impl/semanticEditImportProjection.js +53 -0
  49. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  50. package/dist/internal/index-impl/semanticEditProjectionRecord.js +108 -0
  51. package/dist/internal/index-impl/semanticEditReplayAnchors.js +63 -0
  52. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  53. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  54. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  55. package/dist/internal/index-impl/semanticEditSourceRanges.js +32 -0
  56. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  57. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +92 -9
  58. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +33 -16
  59. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  60. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  61. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  62. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  63. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  64. package/dist/js-ts-safe-member-merge-result.js +158 -0
  65. package/dist/js-ts-safe-member-merge.js +202 -0
  66. package/dist/js-ts-safe-merge-analyze.js +279 -0
  67. package/dist/js-ts-safe-merge-constants.js +50 -0
  68. package/dist/js-ts-safe-merge-context.js +118 -0
  69. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  70. package/dist/js-ts-safe-merge-ledger.js +85 -0
  71. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  72. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  73. package/dist/js-ts-safe-merge-plan.js +190 -0
  74. package/dist/js-ts-safe-merge.js +175 -0
  75. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  76. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  77. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  78. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  79. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  80. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  81. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  82. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  83. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  84. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  85. package/dist/js-ts-semantic-merge-member-source.js +64 -0
  86. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  87. package/dist/js-ts-semantic-merge-parse.js +15 -0
  88. package/dist/js-ts-semantic-merge.js +21 -0
  89. package/dist/lightweight-dependency-effects.js +51 -0
  90. package/dist/lightweight-dependency-language.js +12 -1
  91. package/dist/lightweight-dependency-relations.js +14 -27
  92. package/dist/native-region-scanner-core.js +33 -1
  93. package/dist/native-region-scanner-csharp.js +151 -0
  94. package/dist/native-region-scanner-dart.js +91 -0
  95. package/dist/native-region-scanner-dynamic.js +21 -151
  96. package/dist/native-region-scanner-functional.js +40 -13
  97. package/dist/native-region-scanner-java.js +97 -0
  98. package/dist/native-region-scanner-js-class.js +100 -0
  99. package/dist/native-region-scanner-js-helpers.js +28 -86
  100. package/dist/native-region-scanner-js-imports.js +121 -1
  101. package/dist/native-region-scanner-js-nested.js +96 -8
  102. package/dist/native-region-scanner-js-structure.js +27 -0
  103. package/dist/native-region-scanner-js-types.js +99 -0
  104. package/dist/native-region-scanner-js.js +70 -118
  105. package/dist/native-region-scanner-kotlin.js +94 -0
  106. package/dist/native-region-scanner-main.js +15 -181
  107. package/dist/native-region-scanner-php.js +80 -0
  108. package/dist/native-region-scanner-python.js +62 -0
  109. package/dist/native-region-scanner-ruby.js +72 -0
  110. package/dist/native-region-scanner-scala.js +91 -0
  111. package/dist/native-region-scanner-spans.js +74 -0
  112. package/dist/native-region-scanner-swift.js +155 -0
  113. package/dist/native-region-scanner.js +14 -10
  114. package/dist/native-source-ledger-helpers.js +195 -0
  115. package/dist/native-source-ledger.js +306 -0
  116. package/dist/native-source-preservation-scanner.js +4 -0
  117. package/dist/semantic-import-callsite-regions.js +136 -0
  118. package/dist/semantic-import-effect-regions.js +283 -0
  119. package/dist/semantic-import-regions.js +11 -2
  120. package/dist/semantic-import-sidecar-entry.js +16 -2
  121. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  122. package/dist/semantic-sidecar-example.js +68 -0
  123. package/dist/universal-capability-matrix.js +23 -0
  124. package/dist/universal-conversion-artifact-query.js +79 -2
  125. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  126. package/dist/universal-conversion-artifact-summary.js +33 -1
  127. package/dist/universal-conversion-artifacts.js +13 -48
  128. package/dist/universal-conversion-plan-scoring.js +21 -1
  129. package/dist/universal-conversion-plan-summary.js +30 -0
  130. package/dist/universal-conversion-plan.js +25 -9
  131. package/dist/universal-conversion-route-metadata.js +96 -0
  132. package/dist/universal-conversion-route-operations.js +7 -0
  133. package/dist/universal-representation-coverage.js +193 -0
  134. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { nativeDeclaration } from './native-region-scanner-core.js';
2
- import { jsContainerDelta, jsRegionKindForDeclarationName } from './native-region-scanner-js-helpers.js';
1
+ import { jsControlKeyword, nativeDeclaration, splitParameters } from './native-region-scanner-core.js';
2
+ import { jsContainerDelta, jsContainerInitializerKind, jsRegionKindForDeclarationName } from './native-region-scanner-js-helpers.js';
3
3
 
4
4
  function jsCurrentObjectContext(stack) {
5
5
  return stack[stack.length - 1];
@@ -9,6 +9,21 @@ function jsContextAllowsPropertyScan(context) {
9
9
  return context?.initializerKind === 'object';
10
10
  }
11
11
 
12
+ function jsArrayObjectContextFromLine(context, lineNumber, source) {
13
+ if (context?.initializerKind !== 'array' || context.arrayRecords !== true || !String(source ?? '').trim().startsWith('{')) return undefined;
14
+ const depth = jsContainerDelta(source);
15
+ if (depth <= 0) return undefined;
16
+ const index = (context.nextItemIndex ?? 0) + 1;
17
+ context.nextItemIndex = index;
18
+ return { name: `${context.name}.${index}`, ownerName: context.name, regionKind: context.regionKind, initializerKind: 'object', arrayItem: true, depth, startLine: lineNumber };
19
+ }
20
+
21
+ function updateJsArrayObjectContextName(context, source) {
22
+ if (!context?.arrayItem) return;
23
+ const match = String(source ?? '').trim().match(/^(?:name|id|key|slug|tool|command)\s*:\s*(['"`])([^'"`]+)\1/);
24
+ if (match) context.name = `${context.ownerName}.${match[2]}`;
25
+ }
26
+
12
27
  function jsNestedObjectContextFromDeclaration(declaration, lineNumber, source) {
13
28
  const initializerKind = declaration?.fields?.initializerKind ?? declaration?.metadata?.initializerKind;
14
29
  if (initializerKind !== 'object' && initializerKind !== 'array') return undefined;
@@ -32,9 +47,9 @@ function jsInlineNestedObjectDeclarations(input, lineNumber, source, parentDecla
32
47
  for (const entry of splitTopLevelEntries(body)) {
33
48
  const parsed = parseObjectEntry(entry);
34
49
  if (!parsed) continue;
35
- const initializerKind = nestedInitializerKind(parsed.value);
50
+ const initializerKind = parsed.method ? 'function' : nestedInitializerKind(parsed.value);
36
51
  const name = `${parentDeclaration.name}.${parsed.key}`;
37
- const regionKind = nestedPropertyRegionKind(parentDeclaration, parsed.key, parsed.value);
52
+ const regionKind = initializerKind === 'function' ? 'body' : nestedPropertyRegionKind(parentDeclaration, parsed.key, parsed.value);
38
53
  const declaration = nativeDeclaration(input, lineNumber, initializerKind === 'function' ? 'NestedObjectFunctionProperty' : 'NestedObjectProperty', initializerKind === 'function' ? 'function' : 'property', name, {
39
54
  owner: parentDeclaration.name,
40
55
  propertyName: parsed.key,
@@ -57,6 +72,73 @@ function updateJsObjectContextStack(stack, lineNumber, source) {
57
72
  while (stack.length && stack[stack.length - 1].depth <= 0) stack.pop();
58
73
  }
59
74
 
75
+ function jsObjectPropertyDeclaration(input, lineNumber, trimmed, context) {
76
+ if (/^[}\])]/.test(trimmed) || trimmed.startsWith('...')) return undefined;
77
+ const methodMatch = trimmed.match(/^(?:(?:async|get|set)\s+)?(?:\[\s*(['"`])([^'"`]+)\1\s*\]|(['"`]?)([A-Za-z_$][\w$-]*)\3)\s*\(([^)]*)\)\s*(?:[:\w\s<>\[\]]*)?(?:\{|=>|,|$)/);
78
+ const methodName = methodMatch?.[2] ?? methodMatch?.[4];
79
+ if (methodMatch && !jsControlKeyword(methodName)) {
80
+ const name = `${context.name}.${methodName}`;
81
+ return nativeDeclaration(input, lineNumber, 'ObjectMethod', 'function', name, {
82
+ owner: context.name,
83
+ propertyName: methodName,
84
+ parameters: splitParameters(methodMatch[5])
85
+ }, true, {
86
+ regionKind: 'body',
87
+ metadata: { owner: context.name, propertyName: methodName, initializerKind: 'function' }
88
+ });
89
+ }
90
+ const propertyMatch = trimmed.match(/^(?:\[\s*(['"`])([^'"`]+)\1\s*\]|(['"`])([^'"`]+)\3|([A-Za-z_$][\w$-]*))\s*:\s*(.+?)(?:,)?$/);
91
+ if (!propertyMatch) return undefined;
92
+ const propertyName = propertyMatch[2] ?? propertyMatch[4] ?? propertyMatch[5];
93
+ if (!propertyName || jsControlKeyword(propertyName)) return undefined;
94
+ const value = propertyMatch[6].trim();
95
+ const initializerKind = jsPropertyInitializerKind(value);
96
+ const functionLike = initializerKind === 'function';
97
+ const name = `${context.name}.${propertyName}`;
98
+ return nativeDeclaration(input, lineNumber, functionLike ? 'ObjectFunctionProperty' : 'ObjectProperty', functionLike ? 'function' : 'property', name, {
99
+ owner: context.name,
100
+ propertyName,
101
+ initializerKind
102
+ }, functionLike || initializerKind === 'object' || initializerKind === 'array', {
103
+ regionKind: functionLike ? 'body' : jsPropertyRegionKind(context, propertyName, value),
104
+ metadata: { owner: context.name, propertyName, initializerKind }
105
+ });
106
+ }
107
+
108
+ function jsRouteRecordDeclaration(input, lineNumber, trimmed, context) {
109
+ if (context.regionKind !== 'route') return undefined;
110
+ const match = trimmed.match(/^(?:\{\s*)?(?:path|route|href|url)\s*:\s*(['"`])([^'"`]+)\1/);
111
+ if (!match) return undefined;
112
+ const routePath = match[2];
113
+ return nativeDeclaration(input, lineNumber, 'RouteRecord', 'route', `${context.name}.${routePath}`, {
114
+ owner: context.name,
115
+ routePath
116
+ }, true, {
117
+ regionKind: 'route',
118
+ metadata: { owner: context.name, routePath, initializerKind: 'object' }
119
+ });
120
+ }
121
+
122
+ function jsPropertyInitializerKind(value) {
123
+ const text = String(value ?? '').trim();
124
+ if (/^(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/.test(text)) return 'function';
125
+ const containerKind = jsContainerInitializerKind(text, undefined, text);
126
+ if (containerKind) return containerKind;
127
+ if (/^['"`]/.test(text)) return 'string';
128
+ if (/^(?:true|false)\b/.test(text)) return 'boolean';
129
+ if (/^[0-9]/.test(text)) return 'number';
130
+ return 'expression';
131
+ }
132
+
133
+ function jsPropertyRegionKind(context, propertyName, value) {
134
+ const named = jsRegionKindForDeclarationName(propertyName, value);
135
+ if (named) return named;
136
+ if (context.regionKind === 'route') return 'route';
137
+ if (context.regionKind === 'content') return 'content';
138
+ if (context.regionKind === 'config') return 'config';
139
+ return 'property';
140
+ }
141
+
60
142
  function inlineObjectBody(source) {
61
143
  const text = String(source ?? '');
62
144
  const open = text.indexOf('{');
@@ -113,10 +195,12 @@ function splitTopLevelEntries(body) {
113
195
  }
114
196
 
115
197
  function parseObjectEntry(entry) {
116
- if (!entry || entry.startsWith('...') || entry.startsWith('[')) return undefined;
117
- const match = entry.match(/^(?:(['"])([^'"]+)\1|([A-Za-z_$][\w$-]*))\s*:\s*(.+)$/s);
118
- if (!match) return undefined;
119
- return { key: match[2] ?? match[3], value: match[4].trim() };
198
+ if (!entry || entry.startsWith('...')) return undefined;
199
+ const match = entry.match(/^(?:\[\s*(['"`])([^'"`]+)\1\s*\]|(['"`])([^'"`]+)\3|([A-Za-z_$][\w$-]*))\s*:\s*(.+)$/s);
200
+ if (match) return { key: match[2] ?? match[4] ?? match[5], value: match[6].trim() };
201
+ const methodMatch = entry.match(/^(?:(?:async|get|set)\s+)?(?:\[\s*(['"`])([^'"`]+)\1\s*\]|(['"`]?)([A-Za-z_$][\w$-]*)\3)\s*\(([^)]*)\)\s*(?:[:\w\s<>\[\]]*)?(?:\{|=>|$)/s);
202
+ if (!methodMatch) return undefined;
203
+ return { key: methodMatch[2] ?? methodMatch[4], value: entry.trim(), method: true };
120
204
  }
121
205
 
122
206
  function nestedInitializerKind(value) {
@@ -139,9 +223,13 @@ function nestedPropertyRegionKind(parentDeclaration, propertyName, value) {
139
223
  }
140
224
 
141
225
  export {
226
+ jsArrayObjectContextFromLine,
142
227
  jsContextAllowsPropertyScan,
143
228
  jsCurrentObjectContext,
144
229
  jsInlineNestedObjectDeclarations,
145
230
  jsNestedObjectContextFromDeclaration,
231
+ jsObjectPropertyDeclaration,
232
+ jsRouteRecordDeclaration,
233
+ updateJsArrayObjectContextName,
146
234
  updateJsObjectContextStack
147
235
  };
@@ -0,0 +1,27 @@
1
+ function jsStructureDelta(source) {
2
+ let value = 0;
3
+ let opened = false;
4
+ let quote;
5
+ let escaped = false;
6
+ for (const char of String(source ?? '')) {
7
+ if (quote) {
8
+ if (escaped) escaped = false;
9
+ else if (char === '\\') escaped = true;
10
+ else if (char === quote) quote = undefined;
11
+ continue;
12
+ }
13
+ if (char === '\'' || char === '"' || char === '`') {
14
+ quote = char;
15
+ continue;
16
+ }
17
+ if (char === '{' || char === '[' || char === '(') {
18
+ value += 1;
19
+ opened = true;
20
+ } else if (char === '}' || char === ']' || char === ')') {
21
+ value -= 1;
22
+ }
23
+ }
24
+ return { value, opened };
25
+ }
26
+
27
+ export { jsStructureDelta };
@@ -0,0 +1,99 @@
1
+ import {
2
+ jsControlKeyword,
3
+ nativeDeclaration,
4
+ nativeSignatureDeclaration,
5
+ splitParameters
6
+ } from './native-region-scanner-core.js';
7
+ import { jsRegionKindForDeclarationName } from './native-region-scanner-js-helpers.js';
8
+ import { jsStructureDelta } from './native-region-scanner-js-structure.js';
9
+
10
+ function jsTypeRegionContext(name, declarationLine, lineNumber, regionKind, typeKind) {
11
+ const depth = jsStructureDelta(declarationLine).value;
12
+ if (depth <= 0) return undefined;
13
+ return {
14
+ name,
15
+ typeKind,
16
+ regionKind,
17
+ depth,
18
+ startLine: lineNumber,
19
+ memberStack: []
20
+ };
21
+ }
22
+
23
+ function jsCurrentTypeMemberContext(context) {
24
+ return context.memberStack.at(-1) ?? context;
25
+ }
26
+
27
+ function updateJsTypeRegionContext(context, lineNumber, source) {
28
+ const delta = jsStructureDelta(source).value;
29
+ context.depth += delta;
30
+ for (const memberContext of context.memberStack) {
31
+ if (memberContext.startLine !== lineNumber) memberContext.depth += delta;
32
+ }
33
+ while (context.memberStack.length && context.memberStack.at(-1).depth <= 0) context.memberStack.pop();
34
+ }
35
+
36
+ function jsTypeMemberDeclaration(input, lineNumber, declarationLine, context) {
37
+ const text = String(declarationLine ?? '').trim();
38
+ if (!text || /^[}\])]/.test(text) || text.startsWith('//')) return undefined;
39
+ let match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*([^;,]+))?[;,]?$/);
40
+ if (match && !jsControlKeyword(match[2])) {
41
+ return nativeSignatureDeclaration(input, lineNumber, 'TypeMethodSignature', 'method', `${context.name}.${match[2]}`, {
42
+ owner: context.name,
43
+ propertyName: match[2],
44
+ parameters: splitParameters(match[3]),
45
+ returnType: match[4]?.trim(),
46
+ typeKind: context.typeKind
47
+ }, false, {
48
+ regionKind: jsTypeMemberRegionKind(context, match[2], text),
49
+ metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
50
+ });
51
+ }
52
+ match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*:\s*(.+?)[;,]?$/);
53
+ if (!match || jsControlKeyword(match[2])) return undefined;
54
+ const valueType = match[3].trim();
55
+ const functionLike = /=>/.test(valueType) || /^\([^)]*\)\s*=>/.test(valueType);
56
+ const hasNestedTypeLiteralBody = jsTypeMemberValueStartsTypeLiteral(valueType) && jsStructureDelta(text).value > 0;
57
+ return (functionLike ? nativeSignatureDeclaration : nativeDeclaration)(input, lineNumber, functionLike ? 'TypeFunctionPropertySignature' : 'TypePropertySignature', functionLike ? 'function' : 'property', `${context.name}.${match[2]}`, {
58
+ owner: context.name,
59
+ propertyName: match[2],
60
+ valueType,
61
+ typeKind: context.typeKind
62
+ }, hasNestedTypeLiteralBody, {
63
+ regionKind: jsTypeMemberRegionKind(context, match[2], text),
64
+ metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
65
+ });
66
+ }
67
+
68
+ function jsNestedTypeMemberContextFromDeclaration(declaration, lineNumber, source, parentContext) {
69
+ if (!declaration) return undefined;
70
+ const depth = jsStructureDelta(source).value;
71
+ if (depth <= 0) return undefined;
72
+ const valueType = declaration.fields?.valueType ?? declaration.fields?.returnType;
73
+ const nestedTypeLiteral = jsTypeMemberValueStartsTypeLiteral(valueType);
74
+ return {
75
+ name: nestedTypeLiteral ? declaration.name : parentContext.name,
76
+ typeKind: parentContext.typeKind,
77
+ regionKind: nestedTypeLiteral ? (declaration.regionKind ?? 'property') : parentContext.regionKind,
78
+ depth,
79
+ startLine: lineNumber,
80
+ suppressMembers: !nestedTypeLiteral
81
+ };
82
+ }
83
+
84
+ function jsTypeMemberValueStartsTypeLiteral(valueType) {
85
+ const text = String(valueType ?? '').trim();
86
+ return text.startsWith('{') || /=>\s*\{\s*$/.test(text);
87
+ }
88
+
89
+ function jsTypeMemberRegionKind(context, propertyName) {
90
+ return jsRegionKindForDeclarationName(propertyName) ?? (context.regionKind === 'type' ? 'property' : context.regionKind) ?? 'property';
91
+ }
92
+
93
+ export {
94
+ jsCurrentTypeMemberContext,
95
+ jsNestedTypeMemberContextFromDeclaration,
96
+ jsTypeMemberDeclaration,
97
+ jsTypeRegionContext,
98
+ updateJsTypeRegionContext
99
+ };
@@ -1,40 +1,49 @@
1
1
  import {
2
2
  braceDelta,
3
- jsControlKeyword,
4
- nativeDeclaration,
5
- nativeImportDeclaration,
3
+ nativeDeclaration, nativeImportDeclaration, nativeSignatureDeclaration,
6
4
  sourceLines,
7
5
  splitParameters
8
6
  } from './native-region-scanner-core.js';
9
7
  import {
10
8
  jsCommentOnlyLine,
11
- jsContainerDelta,
12
9
  jsDeclarationScanLine,
13
- jsExportAliasDeclaration,
10
+ jsExportAliasDeclaration, jsExportDeclarations,
14
11
  jsExportedContainerDeclaration,
15
12
  jsExportedFunctionWrapperDeclaration,
16
13
  jsInitializerKind,
17
14
  jsImportDeclarations,
18
- jsObjectPropertyDeclaration,
19
15
  jsObjectRegionContext,
20
16
  jsRegionKindForDeclarationName,
21
- jsRouteRecordDeclaration,
22
17
  jsVariableHasBody,
23
18
  jsVariableSymbolKind
24
19
  } from './native-region-scanner-js-helpers.js';
20
+ import { jsStructureDelta } from './native-region-scanner-js-structure.js';
25
21
  import {
26
- jsContextAllowsPropertyScan,
27
- jsCurrentObjectContext,
28
- jsInlineNestedObjectDeclarations,
29
- jsNestedObjectContextFromDeclaration,
30
- updateJsObjectContextStack
22
+ jsCurrentTypeMemberContext,
23
+ jsNestedTypeMemberContextFromDeclaration,
24
+ jsTypeMemberDeclaration,
25
+ jsTypeRegionContext,
26
+ updateJsTypeRegionContext
27
+ } from './native-region-scanner-js-types.js';
28
+ import {
29
+ jsArrayObjectContextFromLine, jsContextAllowsPropertyScan, jsCurrentObjectContext,
30
+ jsInlineNestedObjectDeclarations, jsNestedObjectContextFromDeclaration,
31
+ jsObjectPropertyDeclaration, jsRouteRecordDeclaration,
32
+ updateJsArrayObjectContextName, updateJsObjectContextStack
31
33
  } from './native-region-scanner-js-nested.js';
34
+ import {
35
+ jsClassMemberDeclaration,
36
+ jsInlineClassMemberDeclarations
37
+ } from './native-region-scanner-js-class.js';
32
38
 
33
39
  function scanJavaScriptLike(input) {
34
40
  const declarations = [];
35
41
  const lines = sourceLines(input.sourceText);
36
42
  const pushDeclaration = (declaration) => {
37
- if (declaration) declarations.push(jsDeclarationWithSourceSpan(input, declaration, lines));
43
+ if (!declaration) return undefined;
44
+ const declarationWithSpan = jsDeclarationWithSourceSpan(input, declaration, lines);
45
+ declarations.push(declarationWithSpan);
46
+ return declarationWithSpan;
38
47
  };
39
48
  const pushDeclarations = (items) => {
40
49
  for (const declaration of items ?? []) pushDeclaration(declaration);
@@ -51,14 +60,22 @@ function scanJavaScriptLike(input) {
51
60
  const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
52
61
  let match;
53
62
  if (currentType && number !== currentType.startLine) {
54
- pushDeclaration(jsTypeMemberDeclaration(input, number, declarationLine, currentType));
63
+ const typeContext = jsCurrentTypeMemberContext(currentType);
64
+ if (!typeContext.suppressMembers) {
65
+ const typeMember = pushDeclaration(jsTypeMemberDeclaration(input, number, declarationLine, typeContext));
66
+ const nestedTypeContext = jsNestedTypeMemberContextFromDeclaration(typeMember, number, declarationLine, typeContext);
67
+ if (nestedTypeContext) currentType.memberStack.push(nestedTypeContext);
68
+ }
55
69
  }
56
70
  const currentObject = jsCurrentObjectContext(objectStack);
57
71
  if (currentObject) {
72
+ const arrayItemContext = jsArrayObjectContextFromLine(currentObject, number, trimmed);
73
+ if (arrayItemContext) objectStack.push(arrayItemContext);
58
74
  const routeRecord = jsRouteRecordDeclaration(input, number, trimmed, currentObject);
59
75
  if (routeRecord) {
60
76
  pushDeclaration(routeRecord);
61
77
  } else if (jsContextAllowsPropertyScan(currentObject)) {
78
+ updateJsArrayObjectContextName(currentObject, trimmed);
62
79
  const property = jsObjectPropertyDeclaration(input, number, trimmed, currentObject);
63
80
  if (property) {
64
81
  pushDeclaration(property);
@@ -74,9 +91,15 @@ function scanJavaScriptLike(input) {
74
91
  } else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
75
92
  pushDeclaration(nativeImportDeclaration(input, number, match[1], 'DynamicImportExpression', 'module'));
76
93
  } else if ((match = declarationLine.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?/))) {
77
- pushDeclaration(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, declarationLine.includes('{')));
94
+ const hasBody = declarationLine.includes('{');
95
+ pushDeclaration((hasBody ? nativeDeclaration : nativeSignatureDeclaration)(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, hasBody));
78
96
  } else if ((match = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?/))) {
79
97
  pushDeclaration(nativeDeclaration(input, number, 'ExportDefaultFunctionDeclaration', 'function', match[1] ?? 'default', { parameters: splitParameters(match[2]), exportDefault: true }, trimmed.includes('{')));
98
+ } else if ((match = trimmed.match(/^export\s+default\s+(?:async\s*)?(?:<[^=]+>\s*)?(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*(?::\s*[^=]+)?=>/))) {
99
+ pushDeclaration(nativeDeclaration(input, number, 'ExportDefaultArrowFunctionDeclaration', 'function', 'default', {
100
+ parameters: splitParameters(match[1] ?? match[2]),
101
+ exportDefault: true
102
+ }, true));
80
103
  } else if ((match = declarationLine.match(/^(default\s+)?(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/)) && (match[1] || match[2])) {
81
104
  const className = match[2] ?? 'default';
82
105
  const exportDefault = Boolean(match[1]);
@@ -121,24 +144,43 @@ function scanJavaScriptLike(input) {
121
144
  if (match.context) objectStack.push(match.context);
122
145
  } else if ((match = jsExportAliasDeclaration(input, number, trimmed))) {
123
146
  pushDeclaration(match);
147
+ } else if ((match = trimmed.match(/^module\.exports\s*=\s*(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*(?:<[^({;]+>)?\s*\(([^)]*)\)/))) {
148
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsDefaultFunctionExport', 'function', match[1] ?? 'module.exports', {
149
+ export: 'commonjs',
150
+ parameters: splitParameters(match[2])
151
+ }, true));
152
+ } else if ((match = trimmed.match(/^module\.exports\s*=\s*(?:async\s*)?(?:<[^=]+>\s*)?(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*(?::\s*[^=]+)?=>/))) {
153
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsDefaultArrowFunctionExport', 'function', 'module.exports', {
154
+ export: 'commonjs',
155
+ parameters: splitParameters(match[1] ?? match[2])
156
+ }, true));
157
+ } else if ((match = trimmed.match(/^module\.exports\s*=\s*(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/))) {
158
+ const className = match[1] ?? 'module.exports';
159
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsDefaultClassExport', 'class', className, { export: 'commonjs' }, trimmed.includes('{')));
160
+ pushDeclarations(jsInlineClassMemberDeclarations(input, number, trimmed, className));
161
+ if (jsStructureDelta(trimmed).value > 0) {
162
+ currentClass = className;
163
+ classDepth = 0;
164
+ }
124
165
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
125
166
  pushDeclaration(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
167
+ } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:<[^=]+>\s*)?(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*(?::\s*[^=]+)?=>/))) {
168
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, true));
169
+ } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/))) {
170
+ const className = match[2] ?? match[1];
171
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsClassExport', 'class', className, { export: 'commonjs', exportName: match[1] }, trimmed.includes('{')));
172
+ pushDeclarations(jsInlineClassMemberDeclarations(input, number, trimmed, className));
173
+ if (jsStructureDelta(trimmed).value > 0) {
174
+ currentClass = className;
175
+ classDepth = 0;
176
+ }
126
177
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
127
178
  const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
128
179
  pushDeclaration(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
129
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
130
- pushDeclaration(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
131
- methodName: match[1],
132
- owner: currentClass,
133
- parameters: splitParameters(match[2])
134
- }, declarationLine.includes('{') || declarationLine.includes('=>')));
135
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*(#?[A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
136
- pushDeclaration(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
137
- propertyName: match[1],
138
- owner: currentClass,
139
- valueType: match[2]?.trim()
140
- }, false, { regionKind: 'property' }));
180
+ } else if (currentClass) {
181
+ pushDeclaration(jsClassMemberDeclaration(input, number, declarationLine, currentClass));
141
182
  }
183
+ pushDeclarations(jsExportDeclarations(input, number, trimmed));
142
184
  if (currentClass) {
143
185
  classDepth += braceDelta(trimmed);
144
186
  if (classDepth <= 0) {
@@ -147,7 +189,7 @@ function scanJavaScriptLike(input) {
147
189
  }
148
190
  }
149
191
  if (currentType) {
150
- if (number !== currentType.startLine) currentType.depth += jsStructureDelta(trimmed).value;
192
+ if (number !== currentType.startLine) updateJsTypeRegionContext(currentType, number, trimmed);
151
193
  if (currentType.depth <= 0) currentType = undefined;
152
194
  }
153
195
  updateJsObjectContextStack(objectStack, number, trimmed);
@@ -192,94 +234,4 @@ function jsBalancedDeclarationEndLine(input, lines, startLine) {
192
234
  return startLine;
193
235
  }
194
236
 
195
- function jsStructureDelta(source) {
196
- let value = 0;
197
- let opened = false;
198
- let quote;
199
- let escaped = false;
200
- for (const char of String(source ?? '')) {
201
- if (quote) {
202
- if (escaped) escaped = false;
203
- else if (char === '\\') escaped = true;
204
- else if (char === quote) quote = undefined;
205
- continue;
206
- }
207
- if (char === '\'' || char === '"' || char === '`') {
208
- quote = char;
209
- continue;
210
- }
211
- if (char === '{' || char === '[' || char === '(') {
212
- value += 1;
213
- opened = true;
214
- } else if (char === '}' || char === ']' || char === ')') {
215
- value -= 1;
216
- }
217
- }
218
- return { value, opened };
219
- }
220
-
221
- function jsTypeRegionContext(name, declarationLine, lineNumber, regionKind, typeKind) {
222
- const depth = jsStructureDelta(declarationLine).value;
223
- if (depth <= 0) return undefined;
224
- return {
225
- name,
226
- typeKind,
227
- regionKind,
228
- depth,
229
- startLine: lineNumber
230
- };
231
- }
232
-
233
- function jsTypeMemberDeclaration(input, lineNumber, declarationLine, context) {
234
- const text = String(declarationLine ?? '').trim();
235
- if (!text || /^[}\])]/.test(text) || text.startsWith('//')) return undefined;
236
- let match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*([^;,]+))?[;,]?$/);
237
- if (match && !jsControlKeyword(match[2])) {
238
- return nativeDeclaration(input, lineNumber, 'TypeMethodSignature', 'method', `${context.name}.${match[2]}`, {
239
- owner: context.name,
240
- propertyName: match[2],
241
- parameters: splitParameters(match[3]),
242
- returnType: match[4]?.trim(),
243
- typeKind: context.typeKind
244
- }, false, {
245
- regionKind: jsTypeMemberRegionKind(context, match[2], text),
246
- metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
247
- });
248
- }
249
- match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*:\s*(.+?)[;,]?$/);
250
- if (!match || jsControlKeyword(match[2])) return undefined;
251
- const valueType = match[3].trim();
252
- const functionLike = /=>/.test(valueType) || /^\([^)]*\)\s*=>/.test(valueType);
253
- return nativeDeclaration(input, lineNumber, functionLike ? 'TypeFunctionPropertySignature' : 'TypePropertySignature', functionLike ? 'function' : 'property', `${context.name}.${match[2]}`, {
254
- owner: context.name,
255
- propertyName: match[2],
256
- valueType,
257
- typeKind: context.typeKind
258
- }, false, {
259
- regionKind: jsTypeMemberRegionKind(context, match[2], text),
260
- metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
261
- });
262
- }
263
-
264
- function jsTypeMemberRegionKind(context, propertyName, source) {
265
- return jsRegionKindForDeclarationName(propertyName, source) ?? (context.regionKind === 'type' ? 'property' : context.regionKind) ?? 'property';
266
- }
267
-
268
- function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, className) {
269
- const open = declarationLine.indexOf('{');
270
- const close = declarationLine.lastIndexOf('}');
271
- if (open < 0 || close <= open) return [];
272
- const body = declarationLine.slice(open + 1, close);
273
- const declarations = [];
274
- for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
275
- if (jsControlKeyword(match[1])) continue;
276
- declarations.push(nativeDeclaration(input, lineNumber, 'MethodDefinition', 'method', `${className}.${match[1]}`, {
277
- methodName: match[1],
278
- owner: className,
279
- parameters: splitParameters(match[2])
280
- }, true));
281
- }
282
- return declarations;
283
- }
284
-
285
237
  export { scanJavaScriptLike };
@@ -0,0 +1,94 @@
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 scanKotlin(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
+ let match;
15
+ if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
16
+ declarations.push(nativeDeclaration(input, number, 'PackageHeader', 'package', match[1], {}, false));
17
+ } else if ((match = trimmed.match(/^import\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\.\*)?)(?:\s+as\s+[A-Za-z_]\w*)?$/))) {
18
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDirective', 'package'));
19
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|sealed|data|value)\s+)*(?:(enum|annotation)\s+)?(class|interface|object)\s+([A-Za-z_]\w*)/))) {
20
+ const owner = nearestContainer(blockStack);
21
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
22
+ const hasBody = trimmed.includes('{');
23
+ declarations.push(nativeDeclaration(input, number, kotlinDeclarationKind(match[2], match[1]), kotlinSymbolKind(match[2], match[1]), name, owner ? { owner: owner.name } : {}, hasBody, spanOptions(input, lines, index, hasBody)));
24
+ if (hasBody) blockStack.push({ kind: match[2], name, bodyDepth: braceDepth + 1 });
25
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|inline|tailrec|operator|infix|external|suspend|override)\s+)*fun\s+(?:<[^>]+>\s*)?(?:([A-Za-z_][\w.<>?]*)\.)?([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
26
+ const owner = nearestContainer(blockStack);
27
+ const receiverType = match[1];
28
+ const target = kotlinFunctionTarget(owner, receiverType, match[2]);
29
+ const hasBody = trimmed.includes('{') || trimmed.includes('=');
30
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', target.owner ? 'method' : 'function', target.name, {
31
+ parameters: splitParameters(match[3]),
32
+ methodName: match[2],
33
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
34
+ ...(receiverType ? { receiverType } : {})
35
+ }, hasBody, {
36
+ ...spanOptions(input, lines, index, trimmed.includes('{')),
37
+ metadata: {
38
+ methodName: match[2],
39
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
40
+ ...(receiverType ? { receiverType } : {})
41
+ }
42
+ }));
43
+ if (trimmed.includes('{')) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
44
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual)\s+)*typealias\s+([A-Za-z_]\w*)\s*=/))) {
45
+ declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
46
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|override|const|lateinit)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
47
+ declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'variable', match[1], {}, false));
48
+ }
49
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
50
+ }
51
+ return declarations;
52
+ }
53
+
54
+ function spanOptions(input, lines, index, hasBraceBody) {
55
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
56
+ }
57
+
58
+ function depthAfterLeadingClosers(trimmed, depth) {
59
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
60
+ return Math.max(0, depth - closers);
61
+ }
62
+
63
+ function nearestContainer(blockStack) {
64
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
65
+ if (blockStack[index].kind === 'function') return undefined;
66
+ if (blockStack[index].kind === 'class' || blockStack[index].kind === 'interface' || blockStack[index].kind === 'object') return blockStack[index];
67
+ }
68
+ return undefined;
69
+ }
70
+
71
+ function kotlinFunctionTarget(owner, receiverType, methodName) {
72
+ if (receiverType) return { name: `${receiverType}.extension.${methodName}`, owner: receiverType, receiverKind: 'extension' };
73
+ if (owner) return { name: `${owner.name}.${methodName}`, owner: owner.name, receiverKind: 'member' };
74
+ return { name: methodName };
75
+ }
76
+
77
+ function braceDelta(source) {
78
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
79
+ }
80
+
81
+ function kotlinDeclarationKind(kind, prefix) {
82
+ if (prefix === 'enum') return 'EnumClassDeclaration';
83
+ if (prefix === 'annotation') return 'AnnotationClassDeclaration';
84
+ return `${upperFirst(kind)}Declaration`;
85
+ }
86
+
87
+ function kotlinSymbolKind(kind, prefix) {
88
+ if (kind === 'interface') return 'interface';
89
+ if (kind === 'object') return 'module';
90
+ if (prefix === 'enum' || prefix === 'annotation') return 'type';
91
+ return 'class';
92
+ }
93
+
94
+ export { scanKotlin };