@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
@@ -6,17 +6,19 @@ import {
6
6
  sourceLines,
7
7
  splitParameters
8
8
  } from './native-region-scanner-core.js';
9
+ import { braceBlockSpan, endKeywordBlockSpan, pythonBlockSpan, terminatedBlockSpan } from './native-region-scanner-spans.js';
9
10
 
10
11
  function scanElixir(input) {
11
12
  const declarations = [];
12
13
  let currentModule;
13
- for (const { line, number } of sourceLines(input.sourceText)) {
14
+ const lines = sourceLines(input.sourceText);
15
+ for (const [index, { line, number }] of lines.entries()) {
14
16
  const trimmed = line.trim();
15
17
  let match;
16
18
  let recordedMeta = false;
17
19
  if ((match = trimmed.match(/^defmodule\s+([A-Z]\w*(?:\.[A-Z]\w*)*)\s+do\b/))) {
18
20
  currentModule = match[1];
19
- declarations.push(nativeDeclaration(input, number, 'ModuleDefinition', 'module', match[1], {}, true));
21
+ declarations.push(nativeDeclaration(input, number, 'ModuleDefinition', 'module', match[1], {}, true, endSpanOptions(input, lines, index)));
20
22
  } else if ((match = trimmed.match(/^(?:alias|import|require)\s+([A-Z]\w*(?:\.[A-Z]\w*)*)/))) {
21
23
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDirective', 'module'));
22
24
  } else if ((match = trimmed.match(/^use\s+([A-Z]\w*(?:\.[A-Z]\w*)*)/))) {
@@ -26,7 +28,8 @@ function scanElixir(input) {
26
28
  declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', match[2]));
27
29
  recordedMeta = true;
28
30
  } else if ((match = trimmed.match(/^defp?\s+([A-Za-z_]\w*[!?]?)\s*(?:\(([^)]*)\)|([^,]*))?/))) {
29
- declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, /\bdo\b/.test(trimmed)));
31
+ const hasDoBlock = /\bdo\b/.test(trimmed);
32
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, hasDoBlock, hasDoBlock ? endSpanOptions(input, lines, index) : {}));
30
33
  } else if (trimmed.startsWith('defstruct')) {
31
34
  declarations.push(nativeDeclaration(input, number, 'StructDefinition', 'type', currentModule ?? `struct_${number}`, {}, true));
32
35
  } else if ((match = trimmed.match(/^@(type|typep|opaque|callback)\s+([A-Za-z_]\w*[!?]?)/))) {
@@ -39,10 +42,19 @@ function scanElixir(input) {
39
42
  return declarations;
40
43
  }
41
44
 
45
+ function endSpanOptions(input, lines, index) {
46
+ return { span: endKeywordBlockSpan(input, lines, index) };
47
+ }
48
+
49
+ function braceSpanOptions(input, lines, index, hasBraceBody) {
50
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
51
+ }
52
+
42
53
  function scanErlang(input) {
43
54
  const declarations = [];
44
55
  const seenFunctions = new Set();
45
- for (const { line, number } of sourceLines(input.sourceText)) {
56
+ const lines = sourceLines(input.sourceText);
57
+ for (const [index, { line, number }] of lines.entries()) {
46
58
  const trimmed = line.trim();
47
59
  let match;
48
60
  let recordedMacro = false;
@@ -70,7 +82,7 @@ function scanErlang(input) {
70
82
  const name = erlangAtomName(match[1]);
71
83
  if (!seenFunctions.has(name)) {
72
84
  seenFunctions.add(name);
73
- declarations.push(nativeDeclaration(input, number, 'FunctionClause', 'function', name, { parameters: splitParameters(match[2]) }, true));
85
+ declarations.push(nativeDeclaration(input, number, 'FunctionClause', 'function', name, { parameters: splitParameters(match[2]) }, true, { span: terminatedBlockSpan(input, lines, index, /\.\s*$/) }));
74
86
  }
75
87
  }
76
88
  if (!recordedMacro && /(^|[^A-Za-z0-9_])\?[A-Za-z_]\w*/.test(trimmed)) {
@@ -83,7 +95,8 @@ function scanErlang(input) {
83
95
  function scanHaskell(input) {
84
96
  const declarations = [];
85
97
  const seenFunctions = new Set();
86
- for (const { line, number } of sourceLines(input.sourceText)) {
98
+ const lines = sourceLines(input.sourceText);
99
+ for (const [index, { line, number }] of lines.entries()) {
87
100
  const trimmed = line.trim();
88
101
  let match;
89
102
  if (/^#\s*(?:if|ifdef|ifndef|else|elif|endif|define|include)\b/.test(trimmed)) {
@@ -106,12 +119,15 @@ function scanHaskell(input) {
106
119
  } else if ((match = trimmed.match(/^class\s+(?:\([^)]*\)\s*=>\s*)?([A-Z][A-Za-z0-9_']*)\b/))) {
107
120
  declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'type', match[1], {}, /\bwhere\b/.test(trimmed)));
108
121
  } else if ((match = trimmed.match(/^([a-z_][A-Za-z0-9_']*)\s*::\s*(.+)$/))) {
109
- seenFunctions.add(match[1]);
110
- declarations.push(nativeDeclaration(input, number, 'FunctionSignature', 'function', match[1], { signature: match[2].trim() }, false));
122
+ declarations.push(nativeDeclaration(input, number, 'FunctionSignature', 'function', match[1], { signature: match[2].trim() }, false, {
123
+ regionKind: 'declaration',
124
+ symbolId: `symbol:${input.language}:signature:${idFragment(match[1])}`,
125
+ metadata: { signatureOnly: true }
126
+ }));
111
127
  } else if ((match = trimmed.match(/^([a-z_][A-Za-z0-9_']*)\b[^=]*=/))) {
112
128
  if (!seenFunctions.has(match[1])) {
113
129
  seenFunctions.add(match[1]);
114
- declarations.push(nativeDeclaration(input, number, 'FunctionBinding', 'function', match[1], {}, true));
130
+ declarations.push(nativeDeclaration(input, number, 'FunctionBinding', 'function', match[1], {}, true, { span: pythonBlockSpan(input, lines, index) }));
115
131
  }
116
132
  }
117
133
  }
@@ -120,7 +136,8 @@ function scanHaskell(input) {
120
136
 
121
137
  function scanR(input) {
122
138
  const declarations = [];
123
- for (const { line, number } of sourceLines(input.sourceText)) {
139
+ const lines = sourceLines(input.sourceText);
140
+ for (const [index, { line, number }] of lines.entries()) {
124
141
  const trimmed = line.trim();
125
142
  let match;
126
143
  if ((match = trimmed.match(/^(?:library|require)\s*\(\s*["']?([A-Za-z_][\w.-]*)["']?/))) {
@@ -130,7 +147,7 @@ function scanR(input) {
130
147
  } else if ((match = trimmed.match(/^source\s*\(\s*["']([^"']+)["']/))) {
131
148
  declarations.push(nativeImportDeclaration(input, number, match[1], 'SourceCall', 'module'));
132
149
  } else if ((match = trimmed.match(/^([A-Za-z_][\w.]*)\s*(?:<-|=)\s*function\s*\(([^)]*)\)/))) {
133
- declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
150
+ declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{'), braceSpanOptions(input, lines, index, trimmed.includes('{'))));
134
151
  } else if ((match = trimmed.match(/^([A-Za-z_][\w.]*)\s*<-\s*R6Class\s*\(\s*["']([^"']+)["']/))) {
135
152
  declarations.push(nativeDeclaration(input, number, 'R6ClassDeclaration', 'class', match[2] || match[1], { binding: match[1] }, true));
136
153
  declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicRuntime', match[2] || match[1]));
@@ -154,9 +171,19 @@ function scanR(input) {
154
171
  }
155
172
 
156
173
  function scanGenericDeclarations(input) {
157
- return sourceLines(input.sourceText)
174
+ const lines = sourceLines(input.sourceText);
175
+ return lines
176
+ .map(({ line, number }, index) => ({ line, number, index }))
158
177
  .filter(({ line }) => /\b(function|class|struct|enum|trait|interface|def)\b/.test(line))
159
- .map(({ line, number }) => nativeDeclaration(input, number, 'NativeDeclaration', 'variable', idFragment(line.trim()).slice(0, 40), { source: line.trim() }, true));
178
+ .map(({ line, number, index }) => nativeDeclaration(input, number, 'NativeDeclaration', 'variable', idFragment(line.trim()).slice(0, 40), { source: line.trim() }, true, genericSpanOptions(input, lines, index)));
179
+ }
180
+
181
+ function genericSpanOptions(input, lines, index) {
182
+ const line = lines[index]?.line.trim() ?? '';
183
+ if (line.includes('{')) return { span: braceBlockSpan(input, lines, index) };
184
+ if (/\b(?:class|module|def|function)\b/.test(line)) return { span: endKeywordBlockSpan(input, lines, index) };
185
+ if (/:\s*$/.test(line)) return { span: pythonBlockSpan(input, lines, index) };
186
+ return {};
160
187
  }
161
188
 
162
189
  function elixirMetaName(source) {
@@ -0,0 +1,97 @@
1
+ import { upperFirst } from './native-import-utils.js';
2
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters, splitTypeParameters } from './native-region-scanner-core.js';
3
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
4
+
5
+ function scanJava(input) {
6
+ const declarations = [];
7
+ const blockStack = [];
8
+ const lines = sourceLines(input.sourceText);
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(/^package\s+([A-Za-z_][\w.]*);/))) {
17
+ declarations.push(nativeDeclaration(input, number, 'PackageDeclaration', 'package', match[1], {}, false));
18
+ } else if ((match = trimmed.match(/^import\s+(?:static\s+)?([A-Za-z_][\w.*]*);/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'package'));
20
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|abstract|final|static|sealed|non-sealed)\s+)*)(class|interface|enum|record|@interface)\s+([A-Za-z_$][\w$]*)/))) {
21
+ const owner = nearestType(blockStack);
22
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
23
+ const kind = match[2] === '@interface' ? 'AnnotationDeclaration' : `${upperFirst(match[2])}Declaration`;
24
+ const hasBody = trimmed.includes('{');
25
+ declarations.push(nativeDeclaration(input, number, kind, javaSymbolKind(match[2]), name, {
26
+ modifiers: javaModifiers(match[1]),
27
+ ...(owner ? { owner: owner.name } : {})
28
+ }, hasBody, spanOptions(input, lines, index, hasBody)));
29
+ if (hasBody) blockStack.push({ kind: javaTypeStackKind(match[2]), name, bodyDepth: braceDepth + 1 });
30
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|abstract|final|static|synchronized|native)\s+)*)(?:<([^>]+)>\s+)?[A-Za-z_$][\w$<>\[\].?,\s]*\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?:throws\s+[^{]+)?(?:\{.*|;)?$/))) {
31
+ const owner = nearestType(blockStack);
32
+ const target = javaMethodTarget(owner, match[1], match[3]);
33
+ const hasBody = trimmed.includes('{');
34
+ declarations.push(nativeDeclaration(input, number, 'MethodDeclaration', target.owner ? 'method' : 'function', target.name, {
35
+ methodName: match[3],
36
+ modifiers: javaModifiers(match[1]),
37
+ typeParameters: splitTypeParameters(match[2]),
38
+ parameters: splitParameters(match[4]),
39
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
40
+ }, hasBody, {
41
+ ...spanOptions(input, lines, index, hasBody),
42
+ metadata: {
43
+ methodName: match[3],
44
+ modifiers: javaModifiers(match[1]),
45
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
46
+ }
47
+ }));
48
+ if (hasBody) blockStack.push({ kind: 'method', name: target.name, bodyDepth: braceDepth + 1 });
49
+ }
50
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
51
+ }
52
+ return declarations;
53
+ }
54
+
55
+ function spanOptions(input, lines, index, hasBraceBody) {
56
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
57
+ }
58
+
59
+ function depthAfterLeadingClosers(trimmed, depth) {
60
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
61
+ return Math.max(0, depth - closers);
62
+ }
63
+
64
+ function nearestType(blockStack) {
65
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
66
+ if (blockStack[index].kind === 'method') return undefined;
67
+ if (['class', 'interface', 'enum', 'record', 'annotation'].includes(blockStack[index].kind)) return blockStack[index];
68
+ }
69
+ return undefined;
70
+ }
71
+
72
+ function javaMethodTarget(owner, modifiers, methodName) {
73
+ if (!owner) return { name: methodName };
74
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
75
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
76
+ }
77
+
78
+ function javaModifiers(raw) {
79
+ return splitParameters(String(raw ?? '').trim().replace(/\s+/g, ','));
80
+ }
81
+
82
+ function javaTypeStackKind(kind) {
83
+ if (kind === '@interface') return 'annotation';
84
+ return String(kind).replace(/\s+/g, ' ');
85
+ }
86
+
87
+ function braceDelta(source) {
88
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
89
+ }
90
+
91
+ function javaSymbolKind(kind) {
92
+ if (kind === 'interface' || kind === '@interface') return 'interface';
93
+ if (kind === 'enum' || kind === 'record') return 'type';
94
+ return 'class';
95
+ }
96
+
97
+ export { scanJava };
@@ -0,0 +1,100 @@
1
+ import { jsControlKeyword, nativeDeclaration, nativeSignatureDeclaration, splitParameters } from './native-region-scanner-core.js';
2
+ import { jsInitializerKind, jsVariableHasBody } from './native-region-scanner-js-helpers.js';
3
+
4
+ function jsClassMemberDeclaration(input, lineNumber, declarationLine, className) {
5
+ const method = jsClassMethodMatch(declarationLine);
6
+ if (method && !jsControlKeyword(method.name)) return jsClassMethodDeclaration(input, lineNumber, method, className);
7
+ const property = jsClassPropertyMatch(declarationLine);
8
+ if (!property || jsControlKeyword(property.name)) return undefined;
9
+ return jsClassPropertyDeclaration(input, lineNumber, property, className);
10
+ }
11
+
12
+ function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, className) {
13
+ const open = declarationLine.indexOf('{');
14
+ const close = declarationLine.lastIndexOf('}');
15
+ if (open < 0 || close <= open) return [];
16
+ const body = declarationLine.slice(open + 1, close);
17
+ const declarations = [];
18
+ const pattern = /((?:(?: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;
19
+ for (const match of body.matchAll(pattern)) {
20
+ const method = { modifiers: match[1], name: match[2], parameters: match[3], source: match[0] };
21
+ if (!jsControlKeyword(method.name)) declarations.push(jsClassMethodDeclaration(input, lineNumber, method, className));
22
+ }
23
+ return declarations;
24
+ }
25
+
26
+ function jsClassMethodMatch(declarationLine) {
27
+ const 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*[^={]+)?(?:\{|=>|$)/);
28
+ if (!match) return undefined;
29
+ return { modifiers: match[1], name: match[2], parameters: match[3], source: declarationLine };
30
+ }
31
+
32
+ function jsClassPropertyMatch(declarationLine) {
33
+ const match = declarationLine.match(/^((?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*)(#?[A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/);
34
+ if (!match) return undefined;
35
+ return { modifiers: match[1], name: match[2], source: declarationLine, valueType: match[3]?.trim() };
36
+ }
37
+
38
+ function jsClassMethodDeclaration(input, lineNumber, method, className) {
39
+ const target = jsClassMemberTarget(className, method.modifiers, method.name);
40
+ const hasBody = String(method.source ?? '').includes('{') || String(method.source ?? '').includes('=>');
41
+ return (hasBody ? nativeDeclaration : nativeSignatureDeclaration)(input, lineNumber, 'MethodDefinition', 'method', target.name, {
42
+ methodName: method.name,
43
+ owner: className,
44
+ receiverKind: target.receiverKind,
45
+ accessorKind: jsAccessorKind(method.modifiers),
46
+ modifiers: jsModifiers(method.modifiers),
47
+ parameters: splitParameters(method.parameters)
48
+ }, hasBody, { metadata: jsClassMemberMetadata(className, target, method.name, method.modifiers) });
49
+ }
50
+
51
+ function jsClassPropertyDeclaration(input, lineNumber, property, className) {
52
+ const initializerKind = jsInitializerKind(property.source ?? '', property.name);
53
+ const hasBody = jsVariableHasBody(initializerKind, property.source ?? '');
54
+ const target = jsClassMemberTarget(className, property.modifiers, property.name);
55
+ return nativeDeclaration(input, lineNumber, 'PropertyDefinition', initializerKind === 'function' ? 'function' : 'property', target.name, {
56
+ propertyName: property.name,
57
+ owner: className,
58
+ receiverKind: target.receiverKind,
59
+ valueType: property.valueType,
60
+ initializerKind,
61
+ modifiers: jsModifiers(property.modifiers)
62
+ }, hasBody, {
63
+ regionKind: initializerKind === 'function' ? 'body' : 'property',
64
+ metadata: {
65
+ initializerKind,
66
+ ...jsClassMemberMetadata(className, target, property.name, property.modifiers)
67
+ }
68
+ });
69
+ }
70
+
71
+ function jsClassMemberTarget(className, modifiers, memberName) {
72
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
73
+ return {
74
+ name: receiverKind === 'static' ? `${className}.static.${memberName}` : `${className}.${memberName}`,
75
+ receiverKind
76
+ };
77
+ }
78
+
79
+ function jsClassMemberMetadata(owner, target, memberName, modifiers) {
80
+ return {
81
+ owner,
82
+ receiverKind: target.receiverKind,
83
+ propertyName: memberName,
84
+ methodName: memberName,
85
+ modifiers: jsModifiers(modifiers),
86
+ accessorKind: jsAccessorKind(modifiers)
87
+ };
88
+ }
89
+
90
+ function jsAccessorKind(modifiers) {
91
+ if (/\bget\b/.test(modifiers)) return 'get';
92
+ if (/\bset\b/.test(modifiers)) return 'set';
93
+ return undefined;
94
+ }
95
+
96
+ function jsModifiers(raw) {
97
+ return String(raw ?? '').trim().split(/\s+/).filter(Boolean);
98
+ }
99
+
100
+ export { jsClassMemberDeclaration, jsInlineClassMemberDeclarations };
@@ -1,9 +1,8 @@
1
1
  import {
2
- jsControlKeyword,
3
2
  nativeDeclaration,
4
3
  splitParameters
5
4
  } from './native-region-scanner-core.js';
6
- import { jsImportDeclarations } from './native-region-scanner-js-imports.js';
5
+ import { jsExportDeclarations, jsImportDeclarations } from './native-region-scanner-js-imports.js';
7
6
  function jsCommentOnlyLine(trimmed) {
8
7
  return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
9
8
  }
@@ -71,13 +70,8 @@ function jsObjectRegionContext(name, declarationLine, lineNumber, regionKind) {
71
70
  if (initializerKind !== 'object' && initializerKind !== 'array') return undefined;
72
71
  const depth = jsContainerDelta(declarationLine);
73
72
  if (depth <= 0) return undefined;
74
- return {
75
- name,
76
- regionKind: regionKind ?? jsRegionKindForDeclarationName(name, declarationLine),
77
- initializerKind,
78
- depth,
79
- startLine: lineNumber
80
- };
73
+ return { name, regionKind: regionKind ?? jsRegionKindForDeclarationName(name, declarationLine), initializerKind, depth, startLine: lineNumber,
74
+ arrayRecords: initializerKind === 'array' && /(?:define|create|make|build)\w*(?:tools?|actions?|handlers?|commands?|events?|effects?|workflows?)/i.test(declarationLine) };
81
75
  }
82
76
 
83
77
  function jsInitializerKind(line, name) {
@@ -127,15 +121,17 @@ function jsExportedContainerDeclaration(input, lineNumber, trimmed) {
127
121
  }
128
122
 
129
123
  function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
130
- const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer)|Object\.freeze)\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
124
+ const match = trimmed.match(/^export\s+default\s+(.+)$/);
131
125
  if (!match) return undefined;
132
- const wrapper = match[1];
133
- const argument = match[2].trim();
126
+ const unwrapped = jsUnwrapFunctionWrapperArgument(match[1]);
127
+ if (!unwrapped) return undefined;
128
+ const { wrapper, wrappers, argument } = unwrapped;
129
+ const wrapperFields = { wrapper, ...(wrappers.length > 1 ? { wrappers } : {}) };
134
130
  let functionMatch = argument.match(/^(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*(?:<[^({;]+>)?\s*\(([^)]*)\)/);
135
131
  if (functionMatch) {
136
132
  return nativeDeclaration(input, lineNumber, 'ExportDefaultFunctionWrapperDeclaration', 'function', functionMatch[1] ?? 'default', {
137
133
  exportDefault: true,
138
- wrapper,
134
+ ...wrapperFields,
139
135
  parameters: splitParameters(functionMatch[2])
140
136
  }, true);
141
137
  }
@@ -143,7 +139,7 @@ function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
143
139
  if (functionMatch) {
144
140
  return nativeDeclaration(input, lineNumber, 'ExportDefaultFunctionWrapperDeclaration', 'function', 'default', {
145
141
  exportDefault: true,
146
- wrapper,
142
+ ...wrapperFields,
147
143
  parameters: splitParameters(functionMatch[1] ?? functionMatch[2])
148
144
  }, true);
149
145
  }
@@ -151,21 +147,33 @@ function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
151
147
  if (classMatch) {
152
148
  return nativeDeclaration(input, lineNumber, 'ExportDefaultClassWrapperDeclaration', 'class', classMatch[1] ?? 'default', {
153
149
  exportDefault: true,
154
- wrapper
150
+ ...wrapperFields
155
151
  }, true);
156
152
  }
157
153
  const aliasMatch = argument.match(/^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*(?:[,)]|$)/);
158
154
  if (!aliasMatch) return undefined;
159
155
  return nativeDeclaration(input, lineNumber, 'ExportDefaultWrappedAlias', 'variable', 'default', {
160
156
  exportDefault: true,
161
- wrapper,
157
+ ...wrapperFields,
162
158
  alias: aliasMatch[1],
163
159
  initializerKind: 'function-wrapper'
164
160
  }, false, {
165
- metadata: { exportDefault: true, wrapper, alias: aliasMatch[1], initializerKind: 'function-wrapper' }
161
+ metadata: { exportDefault: true, ...wrapperFields, alias: aliasMatch[1], initializerKind: 'function-wrapper' }
166
162
  });
167
163
  }
168
164
 
165
+ function jsUnwrapFunctionWrapperArgument(source) {
166
+ const wrappers = [];
167
+ let argument = String(source ?? '').trim();
168
+ let match;
169
+ while ((match = argument.match(/^((?:React\.)?(?:forwardRef|memo|lazy|observer)|Object\.freeze)\s*(?:<[^>]+>)?\s*\(\s*(.+)$/))) {
170
+ wrappers.push(match[1]);
171
+ argument = match[2].trim();
172
+ }
173
+ if (!wrappers.length) return undefined;
174
+ return { wrapper: wrappers[0], wrappers, argument };
175
+ }
176
+
169
177
  function jsExportAliasDeclaration(input, lineNumber, trimmed) {
170
178
  let match = trimmed.match(/^export\s+default\s+([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*;?$/);
171
179
  if (match) return jsAliasExport(input, lineNumber, 'ExportDefaultAlias', 'default', match[1], { exportDefault: true }, trimmed);
@@ -202,72 +210,6 @@ function jsContainerExport(input, lineNumber, languageKind, name, initializer, f
202
210
  return { declaration, context };
203
211
  }
204
212
 
205
- function jsObjectPropertyDeclaration(input, lineNumber, trimmed, context) {
206
- if (/^[}\])]/.test(trimmed) || trimmed.startsWith('...')) return undefined;
207
- const methodMatch = trimmed.match(/^(?:(?:async|get|set)\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\s*\(([^)]*)\)\s*(?:[:\w\s<>\[\]]*)?(?:\{|=>|,|$)/);
208
- if (methodMatch && !jsControlKeyword(methodMatch[2])) {
209
- const name = `${context.name}.${methodMatch[2]}`;
210
- return nativeDeclaration(input, lineNumber, 'ObjectMethod', 'function', name, {
211
- owner: context.name,
212
- propertyName: methodMatch[2],
213
- parameters: splitParameters(methodMatch[3])
214
- }, true, {
215
- regionKind: jsPropertyRegionKind(context, methodMatch[2], 'function'),
216
- metadata: { owner: context.name, propertyName: methodMatch[2], initializerKind: 'function' }
217
- });
218
- }
219
- const propertyMatch = trimmed.match(/^(?:(['"])([^'"]+)\1|([A-Za-z_$][\w$-]*))\s*:\s*(.+?)(?:,)?$/);
220
- if (!propertyMatch) return undefined;
221
- const propertyName = propertyMatch[2] ?? propertyMatch[3];
222
- if (!propertyName || jsControlKeyword(propertyName)) return undefined;
223
- const value = propertyMatch[4].trim();
224
- const initializerKind = jsPropertyInitializerKind(value);
225
- const functionLike = initializerKind === 'function';
226
- const name = `${context.name}.${propertyName}`;
227
- return nativeDeclaration(input, lineNumber, functionLike ? 'ObjectFunctionProperty' : 'ObjectProperty', functionLike ? 'function' : 'property', name, {
228
- owner: context.name,
229
- propertyName,
230
- initializerKind
231
- }, functionLike || initializerKind === 'object' || initializerKind === 'array', {
232
- regionKind: jsPropertyRegionKind(context, propertyName, value),
233
- metadata: { owner: context.name, propertyName, initializerKind }
234
- });
235
- }
236
-
237
- function jsRouteRecordDeclaration(input, lineNumber, trimmed, context) {
238
- if (context.regionKind !== 'route') return undefined;
239
- const match = trimmed.match(/^(?:\{\s*)?(?:path|route|href|url)\s*:\s*(['"`])([^'"`]+)\1/);
240
- if (!match) return undefined;
241
- const routePath = match[2];
242
- return nativeDeclaration(input, lineNumber, 'RouteRecord', 'route', `${context.name}.${routePath}`, {
243
- owner: context.name,
244
- routePath
245
- }, true, {
246
- regionKind: 'route',
247
- metadata: { owner: context.name, routePath, initializerKind: 'object' }
248
- });
249
- }
250
-
251
- function jsPropertyInitializerKind(value) {
252
- const text = String(value ?? '').trim();
253
- if (/^(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/.test(text)) return 'function';
254
- const containerKind = jsContainerInitializerKind(text, undefined, text);
255
- if (containerKind) return containerKind;
256
- if (/^['"`]/.test(text)) return 'string';
257
- if (/^(?:true|false)\b/.test(text)) return 'boolean';
258
- if (/^[0-9]/.test(text)) return 'number';
259
- return 'expression';
260
- }
261
-
262
- function jsPropertyRegionKind(context, propertyName, value) {
263
- const named = jsRegionKindForDeclarationName(propertyName, value);
264
- if (named) return named;
265
- if (context.regionKind === 'route') return 'route';
266
- if (context.regionKind === 'content') return 'content';
267
- if (context.regionKind === 'config') return 'config';
268
- return 'property';
269
- }
270
-
271
213
  function jsContainerInitializerKind(initializer, name, source) {
272
214
  const text = String(initializer ?? '').trim();
273
215
  if (text.startsWith('{')) return 'object';
@@ -279,7 +221,7 @@ function jsContainerInitializerKind(initializer, name, source) {
279
221
 
280
222
  function jsContainerWrapperLooksSemantic(callee, name, source) {
281
223
  const signal = `${callee ?? ''} ${name ?? ''} ${source ?? ''}`.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase();
282
- return /\b(define|create|make|build|object\.freeze)\b/.test(signal) && /\b(config|settings|options|routes?|router|content|docs?|schema|registry|manifest|catalog|menu|nav)\b/.test(signal);
224
+ return /\b(define|create|make|build|object\.freeze)\b/.test(signal) && /\b(actions?|handlers?|tools?|commands?|events?|effects?|workflows?|config|settings|options|routes?|router|content|docs?|schema|registry|manifest|catalog|menu|nav)\b/.test(signal);
283
225
  }
284
226
 
285
227
  function jsContainerDelta(source) {
@@ -316,5 +258,5 @@ function findUnescapedBacktick(text, startIndex) {
316
258
 
317
259
  export { jsCommentOnlyLine, jsContainerDelta, jsDeclarationScanLine };
318
260
  export { jsExportAliasDeclaration, jsExportedContainerDeclaration, jsExportedFunctionWrapperDeclaration };
319
- export { jsInitializerKind, jsImportDeclarations, jsObjectPropertyDeclaration, jsObjectRegionContext };
320
- export { jsRegionKindForDeclarationName, jsRouteRecordDeclaration, jsVariableHasBody, jsVariableSymbolKind };
261
+ export { jsContainerInitializerKind, jsExportDeclarations, jsInitializerKind, jsImportDeclarations, jsObjectRegionContext };
262
+ export { jsRegionKindForDeclarationName, jsVariableHasBody, jsVariableSymbolKind };
@@ -1,7 +1,9 @@
1
1
  import {
2
+ nativeExportDeclaration,
2
3
  nativeImportBindingDeclaration,
3
4
  nativeImportDeclaration
4
5
  } from './native-region-scanner-core.js';
6
+ import { idFragment } from './native-import-utils.js';
5
7
 
6
8
  export function jsImportDeclarations(input, lineNumber, trimmed) {
7
9
  const importEquals = trimmed.match(/^import\s+(type\s+)?([A-Za-z_$][\w$]*)\s*=\s*require\s*\(\s*(['"])([^'"]+)\3\s*\)/);
@@ -28,7 +30,10 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
28
30
  : exportMatch[4]
29
31
  ? jsNamedImportBindings(exportMatch[4], { typeOnly, reexport: true })
30
32
  : [];
31
- return jsImportModuleDeclarations(input, lineNumber, importPath, 'ExportFromDeclaration', bindings, { typeOnly, reexport: true, exportStar: Boolean(exportMatch[2]) });
33
+ return [
34
+ ...jsImportModuleDeclarations(input, lineNumber, importPath, 'ExportFromDeclaration', bindings, { typeOnly, reexport: true, exportStar: Boolean(exportMatch[2]) }),
35
+ ...jsExportModuleDeclarations(input, lineNumber, importPath, bindings, { typeOnly, exportStar: Boolean(exportMatch[2]) })
36
+ ];
32
37
  }
33
38
  const destructuredRequireMatch = trimmed.match(/^(?:const|let|var)\s+\{([^}]+)\}\s*=\s*(?:await\s+)?(require|import)\s*\(\s*(['"])([^'"]+)\3\s*\)/);
34
39
  if (destructuredRequireMatch) {
@@ -54,6 +59,34 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
54
59
  return [];
55
60
  }
56
61
 
62
+ export function jsExportDeclarations(input, lineNumber, trimmed) {
63
+ if (/^export\s+(?:type\s+)?(?:\*|\{[^}]+\}\s+from\b)/.test(trimmed)) return [];
64
+ const named = trimmed.match(/^export\s+(type\s+)?\{([^}]+)\}\s*;?$/);
65
+ if (named) return jsLocalExportDeclarations(input, lineNumber, named[2], { typeOnly: Boolean(named[1]) });
66
+ const namespace = trimmed.match(/^export\s+as\s+namespace\s+([A-Za-z_$][\w$]*)\s*;?$/);
67
+ if (namespace) return [nativeExportDeclaration(input, lineNumber, namespace[1], 'ExportNamespaceDeclaration', {
68
+ exportKind: 'namespace'
69
+ })];
70
+ const assignment = trimmed.match(/^export\s*=\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*;?$/);
71
+ if (assignment) return [nativeExportDeclaration(input, lineNumber, 'module.exports', 'ExportAssignment', {
72
+ exportKind: 'assignment',
73
+ localName: assignment[1]
74
+ }, { name: 'module.exports' })];
75
+ if (!trimmed.startsWith('export ')) return [];
76
+ const defaultMatch = trimmed.match(/^export\s+default\s+(.+)$/);
77
+ if (defaultMatch) return [nativeExportDeclaration(input, lineNumber, 'default', 'ExportDefaultDeclaration', {
78
+ exportDefault: true,
79
+ localName: jsExportedDeclarationName(defaultMatch[1])
80
+ }, { name: 'default' })];
81
+ const declarationSource = trimmed.replace(/^export\s+(?:declare\s+)?/, '');
82
+ if (!jsExportedDeclarationLooksComplete(declarationSource)) return [];
83
+ const exportedName = jsExportedDeclarationName(declarationSource);
84
+ if (!exportedName) return [];
85
+ return [nativeExportDeclaration(input, lineNumber, exportedName, 'ExportNamedDeclaration', {
86
+ declarationKind: jsExportedDeclarationKind(declarationSource)
87
+ })];
88
+ }
89
+
57
90
  function jsDestructuredImportBindings(raw, importKind) {
58
91
  return String(raw ?? '')
59
92
  .split(',')
@@ -100,6 +133,47 @@ function jsImportModuleDeclarations(input, lineNumber, importPath, languageKind,
100
133
  ];
101
134
  }
102
135
 
136
+ function jsExportModuleDeclarations(input, lineNumber, importPath, bindings = [], metadata = {}) {
137
+ const exports = bindings.map((binding) => ({
138
+ localName: binding.importedName,
139
+ exportedName: binding.exportedName ?? binding.localName,
140
+ exportKind: binding.importKind,
141
+ ...(binding.typeOnly ? { typeOnly: true } : {})
142
+ }));
143
+ const statementName = metadata.exportStar
144
+ ? `* from ${importPath}`
145
+ : exports.length
146
+ ? `{${exports.map((binding) => binding.exportedName).join(',')}} from ${importPath}`
147
+ : `from ${importPath}`;
148
+ return [
149
+ nativeExportDeclaration(input, lineNumber, statementName, 'ExportFromDeclaration', {
150
+ importPath: String(importPath),
151
+ exportBindings: exports,
152
+ ...(metadata.typeOnly ? { typeOnly: true } : {}),
153
+ ...(metadata.exportStar ? { exportStar: true } : {})
154
+ }, { symbolKind: 'module', metadata }),
155
+ ...exports.map((binding) => nativeExportDeclaration(input, lineNumber, binding.exportedName, 'ExportBinding', {
156
+ ...binding,
157
+ importPath: String(importPath)
158
+ }, { metadata: { ...binding, importPath: String(importPath) } }))
159
+ ];
160
+ }
161
+
162
+ function jsLocalExportDeclarations(input, lineNumber, raw, options = {}) {
163
+ const bindings = jsNamedExportBindings(raw, options);
164
+ if (!bindings.length) return [];
165
+ return [
166
+ nativeExportDeclaration(input, lineNumber, `{${bindings.map((binding) => binding.exportedName).join(',')}}`, 'ExportNamedDeclaration', {
167
+ exportBindings: bindings,
168
+ ...(options.typeOnly ? { typeOnly: true } : {})
169
+ }, {
170
+ symbolId: `symbol:${input.language}:export:statement:${idFragment(bindings.map((binding) => binding.exportedName).join('_'))}`,
171
+ metadata: { bindingCount: bindings.length, ...(options.typeOnly ? { typeOnly: true } : {}) }
172
+ }),
173
+ ...bindings.map((binding) => nativeExportDeclaration(input, lineNumber, binding.exportedName, 'ExportBinding', binding, { metadata: binding }))
174
+ ];
175
+ }
176
+
103
177
  function jsImportBindingsFromClause(clause, options = {}) {
104
178
  const source = String(clause ?? '').trim();
105
179
  if (!source) return [];
@@ -142,3 +216,49 @@ function jsNamedImportBinding(raw, options = {}) {
142
216
  typeOnly
143
217
  };
144
218
  }
219
+
220
+ function jsNamedExportBindings(raw, options = {}) {
221
+ return String(raw ?? '').split(',').map((part) => jsNamedExportBinding(part, options)).filter(Boolean);
222
+ }
223
+
224
+ function jsNamedExportBinding(raw, options = {}) {
225
+ let text = String(raw ?? '').trim();
226
+ if (!text) return undefined;
227
+ let typeOnly = Boolean(options.typeOnly);
228
+ if (text.startsWith('type ')) {
229
+ typeOnly = true;
230
+ text = text.slice(5).trim();
231
+ }
232
+ const match = text.match(/^([A-Za-z_$][\w$]*|\*)\s*(?:as\s+([A-Za-z_$][\w$]*|\*))?$/);
233
+ if (!match) return undefined;
234
+ const localName = match[1];
235
+ return { localName, exportedName: match[2] ?? localName, exportKind: typeOnly ? 'type-named' : 'named', typeOnly };
236
+ }
237
+
238
+ function jsExportedDeclarationName(source) {
239
+ const text = String(source ?? '').trim();
240
+ return text.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)/)?.[1]
241
+ ?? text.match(/^(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/)?.[1]
242
+ ?? text.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1]
243
+ ?? text.match(/^type\s+([A-Za-z_$][\w$]*)/)?.[1]
244
+ ?? text.match(/^interface\s+([A-Za-z_$][\w$]*)/)?.[1]
245
+ ?? text.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/)?.[1]
246
+ ?? text.match(/^([A-Za-z_$][\w$]*)\b/)?.[1]
247
+ ?? 'default';
248
+ }
249
+
250
+ function jsExportedDeclarationKind(source) {
251
+ const text = String(source ?? '').trim();
252
+ if (/^(?:async\s+)?function/.test(text)) return 'function';
253
+ if (/^(?:abstract\s+)?class/.test(text)) return 'class';
254
+ if (/^interface\b|^type\b|^(?:const\s+)?enum\b/.test(text)) return 'type';
255
+ if (/^(?:const|let|var)\b/.test(text)) return 'variable';
256
+ return 'value';
257
+ }
258
+
259
+ function jsExportedDeclarationLooksComplete(source) {
260
+ const text = String(source ?? '').trim();
261
+ if (/^(?:async\s+)?function/.test(text)) return text.includes(')') && /[;{]/.test(text);
262
+ if (/^(?:abstract\s+)?class/.test(text)) return text.includes('{') || text.endsWith(';');
263
+ return true;
264
+ }