@shapeshift-labs/frontier-lang-compiler 0.2.117 → 0.2.119

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 CHANGED
@@ -238,6 +238,12 @@ console.log(project.outputProjectSymbolGraph.importEdges[0].packageName); // "@p
238
238
  console.log(project.outputProjectSymbolGraph.importEdges[0].packageExportCondition); // "import"
239
239
  ```
240
240
 
241
+ NodeNext-style JS extension specifiers can resolve to supplied TS source files.
242
+ For example, `import './runtime.js'` can resolve to `src/runtime.ts` when that is
243
+ the available project document. Graph edges record `resolutionPathVariant` as
244
+ `"extension-substitution"` so coordinators can distinguish exact source matches
245
+ from source-extension substitutions during stale checks and merge admission.
246
+
241
247
  Package `imports` maps are also modeled for `#internal` specifiers. Top-level
242
248
  `moduleResolution.imports` applies from `packageRoot`/`root`, while
243
249
  `packages[name].imports` applies to the nearest configured package root. Graph
@@ -271,6 +277,13 @@ For `export * from './module.js'`, project graphs fan out re-export identities
271
277
  for each named export in the resolved target document and omit `default`, which
272
278
  matches JavaScript module semantics.
273
279
 
280
+ When using `createTypeScriptCompilerNativeImporterAdapter`, compiler AST imports
281
+ emit the same binding-level module facts instead of only statement-level module
282
+ edges. Default, namespace, named, type-only, side-effect, re-export, export-star,
283
+ local export, `export default`, and `export =` declarations carry `importKind`,
284
+ `exportKind`, `localName`, `importedName`, `exportedName`, `isTypeOnly`, and
285
+ public-contract metadata into the semantic index and project symbol graph.
286
+
274
287
  High-risk native features also have explicit evidence policies. These policies are advisory in this package: they tell a swarm or admission queue what evidence is missing without silently changing the existing readiness classification.
275
288
 
276
289
  ```js
@@ -108,6 +108,7 @@ export interface NativeProjectSymbolGraphModuleEdgeRecord {
108
108
  readonly targetDocumentId?: string;
109
109
  readonly resolvedTargetSymbolId?: string;
110
110
  readonly resolutionKind?: 'relative-source' | 'relative-missing' | 'alias-source' | 'alias-missing' | 'path-alias-source' | 'path-alias-missing' | 'base-url-source' | 'base-url-missing' | 'package-source' | 'package-missing' | 'package-external' | 'package-import-source' | 'package-import-missing' | 'package-import-external' | string;
111
+ readonly resolutionPathVariant?: 'exact' | 'extension-substitution' | 'extensionless' | 'index' | string;
111
112
  readonly packageName?: string; readonly packageSubpath?: string; readonly packageExportCondition?: string;
112
113
  readonly packageImportKey?: string; readonly packageImportCondition?: string; readonly packageImportTarget?: string;
113
114
  readonly importKind?: string;
@@ -240,6 +240,7 @@ function moduleEdgeRecord(relation, moduleEdgeByRelation, symbolsById, documents
240
240
  resolvedModulePath: resolution?.path,
241
241
  targetDocumentId: resolution?.documentId,
242
242
  resolutionKind: resolution?.kind,
243
+ resolutionPathVariant: resolution?.resolutionPathVariant,
243
244
  packageName: resolution?.packageName,
244
245
  packageSubpath: resolution?.packageSubpath,
245
246
  packageExportCondition: resolution?.packageExportCondition,
@@ -253,7 +254,7 @@ function moduleEdgeRecord(relation, moduleEdgeByRelation, symbolsById, documents
253
254
  localName: firstString(moduleEdge.localName, value.localName, symbolMetadata.localName),
254
255
  namespace: firstString(moduleEdge.namespace, value.namespace, symbolMetadata.namespace),
255
256
  exportStar: firstBoolean(moduleEdge.exportStar, value.exportStar, symbolMetadata.exportStar),
256
- isTypeOnly: firstBoolean(moduleEdge.isTypeOnly, value.isTypeOnly),
257
+ isTypeOnly: firstBoolean(moduleEdge.isTypeOnly, value.isTypeOnly, symbolMetadata.isTypeOnly, symbolMetadata.typeOnly),
257
258
  isReExport: firstBoolean(moduleEdge.isReExport, value.isReExport, symbolMetadata.reexport) ?? (relation.predicate === 'exports' && Boolean(moduleSpecifier)),
258
259
  publicContract: firstBoolean(moduleEdge.publicContract, value.publicContract, metadata.publicContract),
259
260
  evidenceIds: uniqueStrings([...(relation.evidenceIds ?? []), ...(fact?.evidenceIds ?? [])])
@@ -0,0 +1,52 @@
1
+ export function modulePathCandidates(path) {
2
+ const normalized = String(path ?? '');
3
+ return uniqueCandidates([
4
+ candidate(normalized, 'exact'),
5
+ ...extensionSubstitutionCandidates(normalized),
6
+ ...extensionlessCandidates(normalized),
7
+ ...indexCandidates(normalized)
8
+ ]);
9
+ }
10
+
11
+ function extensionSubstitutionCandidates(path) {
12
+ const entries = [
13
+ ['.js', ['.ts', '.tsx', '.d.ts', '.jsx']],
14
+ ['.jsx', ['.tsx', '.ts', '.d.ts']],
15
+ ['.mjs', ['.mts', '.ts', '.d.mts']],
16
+ ['.cjs', ['.cts', '.ts', '.d.cts']]
17
+ ];
18
+ for (const [from, targets] of entries) {
19
+ if (!path.endsWith(from)) continue;
20
+ const base = path.slice(0, -from.length);
21
+ return targets.map((target) => candidate(`${base}${target}`, 'extension-substitution'));
22
+ }
23
+ return [];
24
+ }
25
+
26
+ function extensionlessCandidates(path) {
27
+ if (hasKnownExtension(path)) return [];
28
+ return ['.js', '.ts', '.tsx', '.jsx', '.mjs', '.mts', '.cjs', '.cts', '.d.ts']
29
+ .map((extension) => candidate(`${path}${extension}`, 'extensionless'));
30
+ }
31
+
32
+ function indexCandidates(path) {
33
+ return ['.js', '.ts', '.tsx', '.jsx', '.mjs', '.mts', '.cjs', '.cts', '.d.ts']
34
+ .map((extension) => candidate(`${path}/index${extension}`, 'index'));
35
+ }
36
+
37
+ function hasKnownExtension(path) {
38
+ return /\.(?:[cm]?[jt]sx?|d\.[cm]?ts)$/.test(path);
39
+ }
40
+
41
+ function candidate(path, variant) {
42
+ return { path, variant };
43
+ }
44
+
45
+ function uniqueCandidates(candidates) {
46
+ const seen = new Set();
47
+ return candidates.filter((entry) => {
48
+ if (!entry.path || seen.has(entry.path)) return false;
49
+ seen.add(entry.path);
50
+ return true;
51
+ });
52
+ }
@@ -1,3 +1,5 @@
1
+ import { modulePathCandidates } from './projectSymbolGraphModulePathCandidates.js';
2
+
1
3
  export function resolveRelativeProjectModule(sourcePath, moduleSpecifier, documentsByPath) {
2
4
  if (!sourcePath || !moduleSpecifier || !moduleSpecifier.startsWith('.')) return undefined;
3
5
  const base = sourcePath.includes('/') ? sourcePath.slice(0, sourcePath.lastIndexOf('/')) : '';
@@ -6,6 +8,7 @@ export function resolveRelativeProjectModule(sourcePath, moduleSpecifier, docume
6
8
  return {
7
9
  path: target?.path ?? unresolvedPath,
8
10
  documentId: target?.id,
11
+ resolutionPathVariant: target?.resolutionPathVariant,
9
12
  kind: target ? 'relative-source' : 'relative-missing'
10
13
  };
11
14
  }
@@ -69,7 +72,7 @@ function resolveConfiguredProjectModule(moduleSpecifier, documentsByPath, module
69
72
  for (const candidate of candidates) {
70
73
  const target = moduleTargetDocument(candidate.path, documentsByPath);
71
74
  const packageFields = packageResolutionFields(candidate, packageInfo);
72
- if (target) return { path: target.path, documentId: target.id, kind: `${candidate.kind}-source`, ...packageFields };
75
+ if (target) return { path: target.path, documentId: target.id, resolutionPathVariant: target.resolutionPathVariant, kind: `${candidate.kind}-source`, ...packageFields };
73
76
  firstMissing ??= { path: candidate.path, kind: `${candidate.kind}-missing`, ...packageFields };
74
77
  }
75
78
  return firstMissing ?? (packageInfo ? { kind: 'package-external', ...packageInfo } : undefined);
@@ -95,7 +98,7 @@ function resolvePackageImportProjectModule(sourcePath, moduleSpecifier, document
95
98
  packageImportTarget,
96
99
  packageName: packageContext.packageName
97
100
  };
98
- if (resolved) return { path: resolved.path, documentId: resolved.id, kind: 'package-import-source', ...record };
101
+ if (resolved) return { path: resolved.path, documentId: resolved.id, resolutionPathVariant: resolved.resolutionPathVariant, kind: 'package-import-source', ...record };
99
102
  firstMissing ??= { path: candidatePath, kind: 'package-import-missing', ...record };
100
103
  }
101
104
  return firstMissing ?? { kind: 'package-import-external', packageImportKey: match.key };
@@ -224,16 +227,12 @@ function packageFallbackTargets(options, subpath) {
224
227
 
225
228
  function moduleTargetDocument(path, documentsByPath) {
226
229
  for (const candidate of modulePathCandidates(path)) {
227
- const document = documentsByPath.get(candidate);
228
- if (document) return document;
230
+ const document = documentsByPath.get(candidate.path);
231
+ if (document) return { ...document, resolutionPathVariant: candidate.variant };
229
232
  }
230
233
  return undefined;
231
234
  }
232
235
 
233
- function modulePathCandidates(path) {
234
- return [path, `${path}.js`, `${path}.ts`, `${path}.tsx`, `${path}.jsx`, `${path}.d.ts`, `${path}/index.js`, `${path}/index.ts`, `${path}/index.d.ts`];
235
- }
236
-
237
236
  function targetExportName(edge) {
238
237
  const name = edge.importedName ?? edge.localName ?? edge.exportedName;
239
238
  if (!name || name === '*') return undefined;
@@ -15,7 +15,7 @@ export function semanticIndexFromNativeDeclarations(declarations, input, options
15
15
  symbolId,
16
16
  regionKind: declarationRegionKind(declaration)
17
17
  };
18
- const occurrenceId = `occ_${idFragment(declaration.nativeNode.id)}_${declaration.role ?? 'definition'}`;
18
+ const occurrenceId = `occ_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}_${declaration.role ?? 'definition'}`;
19
19
  const ownershipRegion = semanticOwnershipRegionForDeclaration(input, {
20
20
  ...normalizedDeclaration,
21
21
  nodeId: declaration.nativeNode.id,
@@ -24,7 +24,7 @@ export function semanticIndexFromNativeDeclarations(declarations, input, options
24
24
  span: declaration.nativeNode.span,
25
25
  symbolId
26
26
  }, documentId);
27
- const relationId = `rel_${idFragment(documentId)}_${idFragment(declaration.nativeNode.id)}`;
27
+ const relationId = `rel_${idFragment(documentId)}_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}`;
28
28
  const moduleEdge = moduleEdgeForDeclaration(normalizedDeclaration, input, documentId, relationId, ownershipRegion);
29
29
  const publicContractRegion = publicContractRegionForDeclaration(normalizedDeclaration, ownershipRegion, moduleEdge);
30
30
  const reExportIdentity = reExportIdentityForDeclaration(normalizedDeclaration, input, documentId, relationId, ownershipRegion, moduleEdge);
@@ -76,17 +76,17 @@ export function semanticIndexFromNativeDeclarations(declarations, input, options
76
76
  ...(Object.keys(graphMetadata).length ? { metadata: graphMetadata } : {})
77
77
  });
78
78
  facts.push({
79
- id: `fact_${idFragment(declaration.nativeNode.id)}_kind`,
79
+ id: `fact_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}_kind`,
80
80
  predicate: 'nativeKind',
81
81
  subjectId: symbolId,
82
82
  value: declaration.nativeNode.languageKind
83
83
  }, {
84
- id: `fact_${idFragment(declaration.nativeNode.id)}_ownership_region`,
84
+ id: `fact_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}_ownership_region`,
85
85
  predicate: 'semanticOwnershipRegion',
86
86
  subjectId: symbolId,
87
87
  value: ownershipRegion
88
88
  }, {
89
- id: `fact_${idFragment(declaration.nativeNode.id)}_ownership_region_taxonomy`,
89
+ id: `fact_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}_ownership_region_taxonomy`,
90
90
  predicate: 'semanticOwnershipRegionTaxonomy',
91
91
  subjectId: symbolId,
92
92
  value: {
@@ -96,7 +96,7 @@ export function semanticIndexFromNativeDeclarations(declarations, input, options
96
96
  }
97
97
  }, ...projectSymbolGraphFacts({ moduleEdge, publicContractRegion, reExportIdentity, relationId, symbolId, evidenceId }));
98
98
  mappings.push({
99
- id: `map_${idFragment(declaration.nativeNode.id)}`,
99
+ id: `map_${idFragment(declaration.nativeNode.id)}_${idFragment(symbolId)}`,
100
100
  nativeAstNodeId: declaration.nativeNode.id,
101
101
  semanticSymbolId: symbolId,
102
102
  semanticOccurrenceId: occurrenceId,
@@ -197,7 +197,8 @@ function moduleEdgeForDeclaration(declaration, input, documentId, relationId, ow
197
197
  exportedName: declaration.exportedName ?? declaration.metadata?.exportedName,
198
198
  localName: declaration.localName ?? declaration.metadata?.localName,
199
199
  namespace: declaration.namespace ?? declaration.metadata?.namespace,
200
- isTypeOnly: declaration.isTypeOnly ?? declaration.metadata?.isTypeOnly,
200
+ isTypeOnly: declaration.isTypeOnly ?? declaration.metadata?.isTypeOnly ?? declaration.metadata?.typeOnly,
201
+ exportStar: declaration.exportStar ?? declaration.metadata?.exportStar,
201
202
  isReExport: edgeKind === 're-export',
202
203
  publicContract: publicContractForDeclaration(declaration, edgeKind)
203
204
  });
@@ -255,7 +256,8 @@ function reExportIdentityForDeclaration(declaration, input, documentId, relation
255
256
  importedName: declaration.importedName ?? declaration.metadata?.importedName,
256
257
  localName: declaration.localName ?? declaration.metadata?.localName,
257
258
  namespace: declaration.namespace ?? declaration.metadata?.namespace,
258
- isTypeOnly: declaration.isTypeOnly ?? declaration.metadata?.isTypeOnly,
259
+ isTypeOnly: declaration.isTypeOnly ?? declaration.metadata?.isTypeOnly ?? declaration.metadata?.typeOnly,
260
+ exportStar: declaration.exportStar ?? declaration.metadata?.exportStar,
259
261
  symbolId: declaration.symbolId,
260
262
  relationId,
261
263
  ownershipRegionId: ownershipRegion.id,
@@ -267,7 +269,7 @@ function reExportIdentityForDeclaration(declaration, input, documentId, relation
267
269
  function projectSymbolGraphFacts({ moduleEdge, publicContractRegion, reExportIdentity, relationId, symbolId, evidenceId }) {
268
270
  return [
269
271
  moduleEdge ? {
270
- id: `fact_${idFragment(relationId)}_module_edge`,
272
+ id: graphFactId(relationId, symbolId, 'module_edge'),
271
273
  predicate: 'moduleEdge',
272
274
  subjectId: relationId,
273
275
  objectId: symbolId,
@@ -275,7 +277,7 @@ function projectSymbolGraphFacts({ moduleEdge, publicContractRegion, reExportIde
275
277
  evidenceIds: [evidenceId]
276
278
  } : undefined,
277
279
  reExportIdentity ? {
278
- id: `fact_${idFragment(relationId)}_re_export_identity`,
280
+ id: graphFactId(relationId, symbolId, 're_export_identity'),
279
281
  predicate: 'reExportIdentity',
280
282
  subjectId: symbolId,
281
283
  objectId: relationId,
@@ -283,7 +285,7 @@ function projectSymbolGraphFacts({ moduleEdge, publicContractRegion, reExportIde
283
285
  evidenceIds: [evidenceId]
284
286
  } : undefined,
285
287
  publicContractRegion ? {
286
- id: `fact_${idFragment(symbolId)}_public_contract_region`,
288
+ id: graphFactId(relationId, symbolId, 'public_contract_region'),
287
289
  predicate: 'publicContractRegion',
288
290
  subjectId: symbolId,
289
291
  objectId: publicContractRegion.id,
@@ -302,6 +304,10 @@ function hashParts(sourceHash) {
302
304
  };
303
305
  }
304
306
 
307
+ function graphFactId(relationId, symbolId, suffix) {
308
+ return `fact_${idFragment(hashSemanticValue([relationId, symbolId, suffix]))}_${suffix}`;
309
+ }
310
+
305
311
  function compactRecord(record) {
306
312
  return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
307
313
  }
@@ -1,20 +1,30 @@
1
1
  import{hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';import{idFragment}from'../../native-import-utils.js';
2
- import{declarationRecord}from'./declarationRecord.js';import{identifierName}from'./identifierName.js';import{namedDeclaration}from'./namedDeclaration.js';import{stringFromTsExpression}from'./stringFromTsExpression.js';
2
+ import{declarationRecord}from'./declarationRecord.js';import{namedDeclaration}from'./namedDeclaration.js';import{stringFromTsExpression}from'./stringFromTsExpression.js';import{typeScriptExportedDeclarationEntries}from'./typeScriptExportedDeclarationEntries.js';import{typeScriptModuleDeclarationEntries}from'./typeScriptModuleDeclarationEntries.js';
3
3
  export function typeScriptDeclaration(node, kind, nativeNodeId, input, options = {}) {
4
4
  const enrich = (declaration, symbolNode = node.name ?? node) => enrichTypeScriptDeclaration(node, symbolNode, declaration, input, options);
5
+ const moduleEntries = typeScriptModuleDeclarationEntries(node, kind, nativeNodeId, input);
6
+ if (moduleEntries?.length) return moduleEntries.map((entry) => enrich(entry.declaration, entry.symbolNode ?? node));
7
+ const exportedEntries = typeScriptExportedDeclarationEntries(node, kind, nativeNodeId, input, options);
8
+ if (exportedEntries.length && Array.isArray(node.declarationList?.declarations)) return exportedEntries.map((entry) => enrich(entry.declaration, entry.symbolNode ?? node));
5
9
  if (kind === 'ImportDeclaration' || kind === 'ImportEqualsDeclaration') {
6
10
  const name = stringFromTsExpression(node.moduleSpecifier) ?? stringFromTsExpression(node.externalModuleReference?.expression);
7
11
  if (name) return enrich(declarationRecord(input, nativeNodeId, name, 'module', 'import'), node.moduleSpecifier ?? node.externalModuleReference?.expression ?? node);
8
12
  }
9
- if (kind === 'FunctionDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'function'));
10
- if (kind === 'ClassDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'class'));
11
- if (kind === 'InterfaceDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'interface'));
12
- if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'type'));
13
+ if (kind === 'FunctionDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'function')), exportedEntries, enrich, node);
14
+ if (kind === 'ClassDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'class')), exportedEntries, enrich, node);
15
+ if (kind === 'InterfaceDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'interface')), exportedEntries, enrich, node);
16
+ if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'type')), exportedEntries, enrich, node);
13
17
  if (kind === 'VariableDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'variable'));
14
18
  if (kind === 'MethodDeclaration' || kind === 'MethodSignature') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'method'));
15
19
  return undefined;
16
20
  }
17
21
 
22
+ function declarationWithExports(declaration, exportedEntries, enrich, node) {
23
+ const exports = exportedEntries.map((entry) => enrich(entry.declaration, entry.symbolNode ?? node));
24
+ if (!declaration) return exports.length ? exports : undefined;
25
+ return exports.length ? [declaration, ...exports] : declaration;
26
+ }
27
+
18
28
  function enrichTypeScriptDeclaration(node, symbolNode, declaration, input, options) {
19
29
  if (!declaration) return declaration;
20
30
  const checker = options.typeChecker ?? options.checker ?? options.program?.getTypeChecker?.();
@@ -0,0 +1,104 @@
1
+ import{idFragment}from'../../native-import-utils.js';
2
+ import{declarationRecord}from'./declarationRecord.js';import{identifierName}from'./identifierName.js';
3
+ export function typeScriptExportedDeclarationEntries(node, kind, nativeNodeId, input, options = {}) {
4
+ const modifiers = declarationModifiers(node, options.ts);
5
+ if (!modifiers.exported) return [];
6
+ if (isVariableStatement(node, kind)) return variableStatementExportEntries(node, nativeNodeId, input, modifiers);
7
+ const localName = identifierName(node.name);
8
+ const symbolKind = exportedSymbolKind(kind);
9
+ if (!symbolKind && !modifiers.defaulted) return [];
10
+ return [exportEntry(input, nativeNodeId, {
11
+ localName,
12
+ exportedName: modifiers.defaulted ? 'default' : localName,
13
+ exportKind: modifiers.defaulted ? 'default' : exportKindForDeclaration(kind),
14
+ isTypeOnly: typeOnlyDeclarationKind(kind),
15
+ symbolNode: node.name ?? node
16
+ })];
17
+ }
18
+
19
+ function variableStatementExportEntries(node, nativeNodeId, input, modifiers) {
20
+ const declarations = node.declarationList?.declarations ?? [];
21
+ return declarations
22
+ .map((declaration) => {
23
+ const localName = identifierName(declaration.name);
24
+ if (!localName) return undefined;
25
+ return exportEntry(input, nativeNodeId, {
26
+ localName,
27
+ exportedName: modifiers.defaulted ? 'default' : localName,
28
+ exportKind: modifiers.defaulted ? 'default' : 'named',
29
+ isTypeOnly: false,
30
+ symbolNode: declaration.name
31
+ });
32
+ })
33
+ .filter(Boolean);
34
+ }
35
+
36
+ function exportEntry(input, nativeNodeId, binding) {
37
+ return {
38
+ declaration: {
39
+ ...declarationRecord(input, nativeNodeId, binding.exportedName, 'export', 'export'),
40
+ symbolId: `symbol:${input.language}:export:${idFragment(binding.exportedName)}`,
41
+ localName: binding.localName,
42
+ exportedName: binding.exportedName,
43
+ exportKind: binding.exportKind,
44
+ isTypeOnly: binding.isTypeOnly,
45
+ publicContract: true,
46
+ metadata: {
47
+ scan: 'typescript-exported-declaration',
48
+ localName: binding.localName,
49
+ exportedName: binding.exportedName,
50
+ exportKind: binding.exportKind,
51
+ isTypeOnly: binding.isTypeOnly,
52
+ typeOnly: binding.isTypeOnly,
53
+ publicContract: true
54
+ }
55
+ },
56
+ symbolNode: binding.symbolNode
57
+ };
58
+ }
59
+
60
+ function declarationModifiers(node, ts) {
61
+ const helperModifiers = safeModifiers(node, ts);
62
+ const names = new Set([...(node.modifiers ?? []), ...helperModifiers].map((modifier) => modifierName(modifier, ts)));
63
+ return {
64
+ exported: names.has('ExportKeyword') || names.has('export'),
65
+ defaulted: names.has('DefaultKeyword') || names.has('default')
66
+ };
67
+ }
68
+
69
+ function safeModifiers(node, ts) {
70
+ try {
71
+ if (typeof ts?.canHaveModifiers === 'function' && !ts.canHaveModifiers(node)) return [];
72
+ return typeof ts?.getModifiers === 'function' ? [...(ts.getModifiers(node) ?? [])] : [];
73
+ } catch {
74
+ return [];
75
+ }
76
+ }
77
+
78
+ function isVariableStatement(node, kind) {
79
+ return kind === 'VariableStatement' || Array.isArray(node.declarationList?.declarations);
80
+ }
81
+
82
+ function modifierName(modifier, ts) {
83
+ if (typeof modifier.kindName === 'string') return modifier.kindName;
84
+ if (ts?.SyntaxKind && modifier.kind !== undefined) return ts.SyntaxKind[modifier.kind] ?? String(modifier.kind);
85
+ if (typeof modifier.text === 'string') return modifier.text;
86
+ if (typeof modifier.name === 'string') return modifier.name;
87
+ return String(modifier.kind ?? '');
88
+ }
89
+
90
+ function exportedSymbolKind(kind) {
91
+ if (kind === 'FunctionDeclaration') return 'function';
92
+ if (kind === 'ClassDeclaration') return 'class';
93
+ if (kind === 'InterfaceDeclaration') return 'interface';
94
+ if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return 'type';
95
+ return undefined;
96
+ }
97
+
98
+ function exportKindForDeclaration(kind) {
99
+ return typeOnlyDeclarationKind(kind) ? 'type-named' : 'named';
100
+ }
101
+
102
+ function typeOnlyDeclarationKind(kind) {
103
+ return kind === 'InterfaceDeclaration' || kind === 'TypeAliasDeclaration';
104
+ }
@@ -0,0 +1,279 @@
1
+ import{idFragment}from'../../native-import-utils.js';
2
+ import{declarationRecord}from'./declarationRecord.js';import{identifierName}from'./identifierName.js';import{stringFromTsExpression}from'./stringFromTsExpression.js';
3
+ export function typeScriptModuleDeclarationEntries(node, kind, nativeNodeId, input) {
4
+ if (kind === 'ImportDeclaration') return importDeclarationEntries(node, nativeNodeId, input);
5
+ if (kind === 'ImportEqualsDeclaration') return importEqualsDeclarationEntries(node, nativeNodeId, input);
6
+ if (kind === 'ExportDeclaration') return exportDeclarationEntries(node, nativeNodeId, input);
7
+ if (kind === 'ExportAssignment') return exportAssignmentEntries(node, nativeNodeId, input);
8
+ return undefined;
9
+ }
10
+
11
+ function importDeclarationEntries(node, nativeNodeId, input) {
12
+ const moduleSpecifier = stringFromTsExpression(node.moduleSpecifier);
13
+ if (!moduleSpecifier) return [];
14
+ const typeOnly = Boolean(node.importClause?.isTypeOnly);
15
+ const bindings = importClauseBindings(node.importClause, typeOnly);
16
+ return importModuleEntries(input, nativeNodeId, moduleSpecifier, 'ImportDeclaration', bindings, {
17
+ typeOnly,
18
+ sideEffectOnly: bindings.length === 0,
19
+ importKind: bindings.length === 0 ? 'side-effect' : 'module'
20
+ });
21
+ }
22
+
23
+ function importEqualsDeclarationEntries(node, nativeNodeId, input) {
24
+ const moduleSpecifier = stringFromTsExpression(node.moduleReference?.expression ?? node.externalModuleReference?.expression);
25
+ const localName = identifierName(node.name);
26
+ if (!moduleSpecifier || !localName) return [];
27
+ const typeOnly = Boolean(node.isTypeOnly);
28
+ return importModuleEntries(input, nativeNodeId, moduleSpecifier, 'ImportEqualsDeclaration', [{
29
+ localName,
30
+ importedName: 'default',
31
+ importKind: 'commonjs-require',
32
+ isTypeOnly: typeOnly,
33
+ symbolNode: node.name
34
+ }], { typeOnly, importKind: 'commonjs-require' });
35
+ }
36
+
37
+ function exportDeclarationEntries(node, nativeNodeId, input) {
38
+ const moduleSpecifier = stringFromTsExpression(node.moduleSpecifier);
39
+ const typeOnly = Boolean(node.isTypeOnly);
40
+ const exportStar = moduleSpecifier && !node.exportClause;
41
+ const bindings = exportClauseBindings(node.exportClause, { typeOnly, reExport: Boolean(moduleSpecifier) });
42
+ return [
43
+ ...(moduleSpecifier ? importModuleEntries(input, nativeNodeId, moduleSpecifier, 'ExportFromDeclaration', reExportImportBindings(bindings, exportStar), {
44
+ typeOnly,
45
+ reexport: true,
46
+ exportStar,
47
+ importKind: exportStar ? 'reexport' : 'reexport'
48
+ }) : []),
49
+ ...exportModuleEntries(input, nativeNodeId, moduleSpecifier, bindings, {
50
+ typeOnly,
51
+ exportStar,
52
+ reExport: Boolean(moduleSpecifier),
53
+ exportKind: exportStar ? 'export-star' : 'named'
54
+ })
55
+ ];
56
+ }
57
+
58
+ function exportAssignmentEntries(node, nativeNodeId, input) {
59
+ const exportedName = node.isExportEquals ? 'module.exports' : 'default';
60
+ const localName = stringFromTsExpression(node.expression);
61
+ return [{
62
+ declaration: compactRecord({
63
+ ...declarationRecord(input, nativeNodeId, exportedName, 'export', 'export'),
64
+ exportedName,
65
+ localName,
66
+ exportKind: node.isExportEquals ? 'assignment' : 'default',
67
+ publicContract: true,
68
+ metadata: compactRecord({
69
+ exportKind: node.isExportEquals ? 'assignment' : 'default',
70
+ exportedName,
71
+ localName,
72
+ publicContract: true
73
+ })
74
+ }),
75
+ symbolNode: node.expression ?? node
76
+ }];
77
+ }
78
+
79
+ function importClauseBindings(importClause, typeOnly) {
80
+ if (!importClause) return [];
81
+ const bindings = [];
82
+ const defaultName = identifierName(importClause.name);
83
+ if (defaultName) bindings.push({
84
+ localName: defaultName,
85
+ importedName: 'default',
86
+ importKind: typeOnly ? 'type-default' : 'default',
87
+ isTypeOnly: typeOnly,
88
+ symbolNode: importClause.name
89
+ });
90
+ const named = importClause.namedBindings;
91
+ if (!named) return bindings;
92
+ if (Array.isArray(named.elements)) {
93
+ bindings.push(...named.elements.map((element) => importSpecifierBinding(element, typeOnly)).filter(Boolean));
94
+ } else {
95
+ const namespace = identifierName(named.name);
96
+ if (namespace) bindings.push({ localName: namespace, importedName: '*', namespace, importKind: 'namespace', isTypeOnly: typeOnly, symbolNode: named.name });
97
+ }
98
+ return bindings;
99
+ }
100
+
101
+ function importSpecifierBinding(element, parentTypeOnly) {
102
+ const localName = identifierName(element.name);
103
+ if (!localName) return undefined;
104
+ const typeOnly = Boolean(parentTypeOnly || element.isTypeOnly);
105
+ const importedName = identifierName(element.propertyName) ?? localName;
106
+ return {
107
+ localName,
108
+ importedName,
109
+ importKind: typeOnly ? 'type-named' : 'named',
110
+ isTypeOnly: typeOnly,
111
+ symbolNode: element.name
112
+ };
113
+ }
114
+
115
+ function exportClauseBindings(exportClause, options) {
116
+ if (!exportClause) return [];
117
+ if (Array.isArray(exportClause.elements)) {
118
+ return exportClause.elements.map((element) => exportSpecifierBinding(element, options)).filter(Boolean);
119
+ }
120
+ const exportedName = identifierName(exportClause.name);
121
+ if (!exportedName) return [];
122
+ return [{
123
+ localName: exportedName,
124
+ importedName: '*',
125
+ exportedName,
126
+ namespace: exportedName,
127
+ importKind: 'namespace-reexport',
128
+ exportKind: 'namespace-reexport',
129
+ isTypeOnly: Boolean(options.typeOnly),
130
+ reExport: Boolean(options.reExport),
131
+ symbolNode: exportClause.name
132
+ }];
133
+ }
134
+
135
+ function exportSpecifierBinding(element, options) {
136
+ const exportedName = identifierName(element.name);
137
+ if (!exportedName) return undefined;
138
+ const typeOnly = Boolean(options.typeOnly || element.isTypeOnly);
139
+ const localName = identifierName(element.propertyName) ?? exportedName;
140
+ return {
141
+ localName,
142
+ importedName: options.reExport ? localName : undefined,
143
+ exportedName,
144
+ importKind: options.reExport ? typeOnly ? 'type-reexport' : 'reexport' : undefined,
145
+ exportKind: typeOnly ? 'type-named' : 'named',
146
+ isTypeOnly: typeOnly,
147
+ reExport: Boolean(options.reExport),
148
+ symbolNode: element.name
149
+ };
150
+ }
151
+
152
+ function reExportImportBindings(bindings, exportStar) {
153
+ if (exportStar) return [];
154
+ return bindings.map((binding) => ({
155
+ localName: binding.exportedName ?? binding.localName,
156
+ importedName: binding.importedName ?? binding.localName,
157
+ exportedName: binding.exportedName,
158
+ namespace: binding.namespace,
159
+ importKind: binding.importKind ?? 'reexport',
160
+ isTypeOnly: binding.isTypeOnly,
161
+ symbolNode: binding.symbolNode
162
+ }));
163
+ }
164
+
165
+ function importModuleEntries(input, nativeNodeId, moduleSpecifier, _languageKind, bindings, metadata) {
166
+ const moduleEntry = {
167
+ declaration: compactRecord({
168
+ ...declarationRecord(input, nativeNodeId, moduleSpecifier, 'module', 'import'),
169
+ importPath: moduleSpecifier,
170
+ moduleSpecifier,
171
+ importKind: metadata.importKind,
172
+ isTypeOnly: metadata.typeOnly,
173
+ exportStar: metadata.exportStar,
174
+ metadata: moduleMetadata('typescript-import', bindings, metadata)
175
+ })
176
+ };
177
+ return [moduleEntry, ...bindings.map((binding) => importBindingEntry(input, nativeNodeId, moduleSpecifier, binding))];
178
+ }
179
+
180
+ function importBindingEntry(input, nativeNodeId, moduleSpecifier, binding) {
181
+ const declaration = compactRecord({
182
+ ...declarationRecord(input, nativeNodeId, binding.localName, 'import', 'import'),
183
+ symbolId: `symbol:${input.language}:import:${idFragment(`${moduleSpecifier}:${binding.localName}:${binding.importedName}`)}`,
184
+ importPath: moduleSpecifier,
185
+ moduleSpecifier,
186
+ localName: binding.localName,
187
+ importedName: binding.importedName,
188
+ exportedName: binding.exportedName,
189
+ namespace: binding.namespace,
190
+ importKind: binding.importKind,
191
+ isTypeOnly: binding.isTypeOnly,
192
+ metadata: compactRecord({
193
+ scan: 'typescript-import-binding',
194
+ moduleSpecifier,
195
+ importPath: moduleSpecifier,
196
+ localName: binding.localName,
197
+ importedName: binding.importedName,
198
+ exportedName: binding.exportedName,
199
+ namespace: binding.namespace,
200
+ importKind: binding.importKind,
201
+ isTypeOnly: binding.isTypeOnly,
202
+ typeOnly: binding.isTypeOnly
203
+ })
204
+ });
205
+ return { declaration, symbolNode: binding.symbolNode };
206
+ }
207
+
208
+ function exportModuleEntries(input, nativeNodeId, moduleSpecifier, bindings, metadata) {
209
+ const statementName = metadata.exportStar
210
+ ? `* from ${moduleSpecifier}`
211
+ : moduleSpecifier ? `{${bindings.map((binding) => binding.exportedName).join(',')}} from ${moduleSpecifier}` : `{${bindings.map((binding) => binding.exportedName).join(',')}}`;
212
+ const statement = {
213
+ declaration: compactRecord({
214
+ ...declarationRecord(input, nativeNodeId, statementName, 'module', 'export'),
215
+ exportPath: moduleSpecifier,
216
+ moduleSpecifier,
217
+ exportKind: metadata.exportKind,
218
+ exportStar: metadata.exportStar,
219
+ isTypeOnly: metadata.typeOnly,
220
+ reExport: metadata.reExport,
221
+ publicContract: true,
222
+ metadata: moduleMetadata('typescript-export', bindings, metadata)
223
+ })
224
+ };
225
+ return [statement, ...bindings.map((binding) => exportBindingEntry(input, nativeNodeId, moduleSpecifier, binding))];
226
+ }
227
+
228
+ function exportBindingEntry(input, nativeNodeId, moduleSpecifier, binding) {
229
+ const declaration = compactRecord({
230
+ ...declarationRecord(input, nativeNodeId, binding.exportedName, 'export', 'export'),
231
+ symbolId: `symbol:${input.language}:export:${idFragment(`${moduleSpecifier ?? 'local'}:${binding.exportedName}:${binding.localName}`)}`,
232
+ exportPath: moduleSpecifier,
233
+ moduleSpecifier,
234
+ localName: binding.localName,
235
+ importedName: binding.importedName,
236
+ exportedName: binding.exportedName,
237
+ namespace: binding.namespace,
238
+ exportKind: binding.exportKind,
239
+ isTypeOnly: binding.isTypeOnly,
240
+ reExport: binding.reExport,
241
+ publicContract: true,
242
+ metadata: compactRecord({
243
+ scan: 'typescript-export-binding',
244
+ moduleSpecifier,
245
+ exportPath: moduleSpecifier,
246
+ localName: binding.localName,
247
+ importedName: binding.importedName,
248
+ exportedName: binding.exportedName,
249
+ namespace: binding.namespace,
250
+ exportKind: binding.exportKind,
251
+ isTypeOnly: binding.isTypeOnly,
252
+ typeOnly: binding.isTypeOnly,
253
+ reExport: binding.reExport,
254
+ publicContract: true
255
+ })
256
+ });
257
+ return { declaration, symbolNode: binding.symbolNode };
258
+ }
259
+
260
+ function moduleMetadata(scan, bindings, metadata) {
261
+ return compactRecord({
262
+ scan,
263
+ moduleOnly: true,
264
+ bindingCount: bindings.length,
265
+ importKind: metadata.importKind,
266
+ exportKind: metadata.exportKind,
267
+ isTypeOnly: metadata.typeOnly,
268
+ typeOnly: metadata.typeOnly,
269
+ sideEffectOnly: metadata.sideEffectOnly,
270
+ reexport: metadata.reexport,
271
+ reExport: metadata.reExport,
272
+ exportStar: metadata.exportStar,
273
+ publicContract: metadata.reExport || metadata.exportKind
274
+ });
275
+ }
276
+
277
+ function compactRecord(record) {
278
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
279
+ }
@@ -23,17 +23,20 @@ export function visitTypeScriptAstNode(node, sourceFile, context, propertyPath,
23
23
  } else if (Array.isArray(node.children)) {
24
24
  node.children.forEach(visitChild);
25
25
  }
26
- const declaration = typeScriptDeclaration(node, kind, id, context.input, context.options);
26
+ const declarationResult = typeScriptDeclaration(node, kind, id, context.input, context.options);
27
+ const declarations = Array.isArray(declarationResult) ? declarationResult.filter(Boolean) : declarationResult ? [declarationResult] : [];
28
+ const primaryDeclaration = declarations[0];
27
29
  const nativeNode = {
28
30
  id,
29
31
  kind,
30
32
  languageKind: `${context.input.language}.${kind}`,
31
33
  span,
32
- value: declaration?.name ?? typeScriptNodeValue(node),
34
+ value: primaryDeclaration?.name ?? typeScriptNodeValue(node),
33
35
  fields: primitiveTypeScriptFields(node, kind),
34
36
  children,
35
37
  metadata: {
36
- ...declaration?.metadata,
38
+ ...primaryDeclaration?.metadata,
39
+ ...(declarations.length > 1 ? { declarationCount: declarations.length } : {}),
37
40
  astFormat: context.options.astFormat,
38
41
  propertyPath,
39
42
  pos: numberOrUndefined(node.pos),
@@ -41,6 +44,6 @@ export function visitTypeScriptAstNode(node, sourceFile, context, propertyPath,
41
44
  }
42
45
  };
43
46
  context.nodes[id] = nativeNode;
44
- if (declaration) context.declarations.push({ ...declaration, nativeNode });
47
+ for (const declaration of declarations) context.declarations.push({ ...declaration, nativeNode });
45
48
  return id;
46
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.117",
3
+ "version": "0.2.119",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",