@shapeshift-labs/frontier-lang-compiler 0.2.144 → 0.2.146

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.
@@ -15,6 +15,29 @@ export function syntaxDeclaration(node, nativeNodeId, input) {
15
15
  if (kind === 'ClassDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'class');
16
16
  if (kind === 'TSInterfaceDeclaration' || kind === 'InterfaceDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'interface');
17
17
  if (kind === 'TSTypeAliasDeclaration' || kind === 'TypeAliasDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'type');
18
+ if (kind === 'TSModuleDeclaration' || kind === 'ModuleDeclaration') return syntaxModuleDeclaration(input, nativeNodeId, node);
18
19
  if (kind === 'VariableDeclarator') return namedDeclaration(input, nativeNodeId, node.id, 'variable');
19
20
  return undefined;
20
21
  }
22
+
23
+ function syntaxModuleDeclaration(input, nativeNodeId, node) {
24
+ const name = syntaxName(node.id ?? node.name);
25
+ if (!name) return undefined;
26
+ return {
27
+ ...declarationRecord(input, nativeNodeId, name, 'module', 'definition'),
28
+ metadata: {
29
+ scan: 'syntax-module-declaration',
30
+ moduleName: name,
31
+ namespace: name
32
+ }
33
+ };
34
+ }
35
+
36
+ function syntaxName(node) {
37
+ if (!node) return undefined;
38
+ if (typeof node === 'string') return node;
39
+ if (typeof node.name === 'string') return node.name;
40
+ if (typeof node.value === 'string') return node.value;
41
+ if (typeof node.text === 'string') return node.text;
42
+ return undefined;
43
+ }
@@ -14,6 +14,7 @@ export function typeScriptDeclaration(node, kind, nativeNodeId, input, options =
14
14
  if (kind === 'ClassDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'class')), exportedEntries, enrich, node);
15
15
  if (kind === 'InterfaceDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'interface')), exportedEntries, enrich, node);
16
16
  if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'type')), exportedEntries, enrich, node);
17
+ if (kind === 'ModuleDeclaration') return declarationWithExports(enrich(moduleDeclaration(input, nativeNodeId, node), node.name ?? node), exportedEntries, enrich, node);
17
18
  if (kind === 'VariableDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'variable'));
18
19
  if (kind === 'MethodDeclaration' || kind === 'MethodSignature') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'method'));
19
20
  return undefined;
@@ -25,6 +26,19 @@ function declarationWithExports(declaration, exportedEntries, enrich, node) {
25
26
  return exports.length ? [declaration, ...exports] : declaration;
26
27
  }
27
28
 
29
+ function moduleDeclaration(input, nativeNodeId, node) {
30
+ const name = stringFromTsExpression(node.name);
31
+ if (!name) return undefined;
32
+ return {
33
+ ...declarationRecord(input, nativeNodeId, name, 'module', 'definition'),
34
+ metadata: compactRecord({
35
+ scan: 'typescript-module-declaration',
36
+ moduleName: name,
37
+ namespace: name
38
+ })
39
+ };
40
+ }
41
+
28
42
  function enrichTypeScriptDeclaration(node, symbolNode, declaration, input, options) {
29
43
  if (!declaration) return declaration;
30
44
  const checker = options.typeChecker ?? options.checker ?? options.program?.getTypeChecker?.();
@@ -92,6 +92,7 @@ function exportedSymbolKind(kind) {
92
92
  if (kind === 'ClassDeclaration') return 'class';
93
93
  if (kind === 'InterfaceDeclaration') return 'interface';
94
94
  if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return 'type';
95
+ if (kind === 'ModuleDeclaration') return 'module';
95
96
  return undefined;
96
97
  }
97
98
 
@@ -1,6 +1,7 @@
1
1
  import { idFragment } from './native-import-utils.js';
2
2
  import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
3
3
  import { compactRecord } from './js-ts-safe-merge-context.js';
4
+ import { augmentProjectSymbolGraphPublicContracts } from './js-ts-safe-project-merge-public-contracts.js';
4
5
  import { createNativeProjectImportResult } from './internal/index-impl/createNativeProjectImportResult.js';
5
6
  import { importNativeSource } from './internal/index-impl/importNativeSource.js';
6
7
  import {
@@ -92,22 +93,26 @@ function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stag
92
93
  ...(stageName === 'output' ? { outputProjectImportSource: projectGraphImportSource } : {})
93
94
  }
94
95
  }, imports);
95
- const edgeLimitConflicts = projectGraphEdgeLimitConflicts(limits, stageName, projectImport.projectSymbolGraph);
96
+ const projectSymbolGraph = augmentProjectSymbolGraphPublicContracts(projectImport.projectSymbolGraph, files, input, stageName);
97
+ const stageProjectImport = projectSymbolGraph === projectImport.projectSymbolGraph
98
+ ? projectImport
99
+ : attachProjectSymbolGraph(projectImport, projectSymbolGraph);
100
+ const edgeLimitConflicts = projectGraphEdgeLimitConflicts(limits, stageName, projectSymbolGraph);
96
101
  const serialized = projectGraphSerializedLimitConflict(limits, stageName, {
97
- projectImport,
98
- projectSymbolGraph: projectImport.projectSymbolGraph
102
+ projectImport: stageProjectImport,
103
+ projectSymbolGraph
99
104
  });
100
105
  const limitConflicts = [...edgeLimitConflicts, serialized.conflict].filter(Boolean);
101
106
  if (limitConflicts.length) {
102
- return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, projectImport.projectSymbolGraph, limitConflicts, serialized.serializedBytes);
107
+ return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, projectSymbolGraph, limitConflicts, serialized.serializedBytes);
103
108
  }
104
109
  return {
105
110
  kind: 'frontier.lang.jsTsProjectGraphStage',
106
111
  version: 1,
107
112
  stage: stageName,
108
- projectImport,
109
- projectSymbolGraph: projectImport.projectSymbolGraph,
110
- summary: projectGraphStageSummary(stageName, projectImport.projectSymbolGraph, projectGraphImportSource, sourceStats, serialized.serializedBytes, [])
113
+ projectImport: stageProjectImport,
114
+ projectSymbolGraph,
115
+ summary: projectGraphStageSummary(stageName, projectSymbolGraph, projectGraphImportSource, sourceStats, serialized.serializedBytes, [])
111
116
  };
112
117
  }
113
118
 
@@ -215,6 +220,24 @@ function stageFile(file, sourceText, input) {
215
220
  });
216
221
  }
217
222
 
223
+ function attachProjectSymbolGraph(projectImport, projectSymbolGraph) {
224
+ return {
225
+ ...projectImport,
226
+ projectSymbolGraph,
227
+ semanticIndex: projectImport.semanticIndex ? {
228
+ ...projectImport.semanticIndex,
229
+ metadata: {
230
+ ...projectImport.semanticIndex.metadata,
231
+ projectSymbolGraph
232
+ }
233
+ } : projectImport.semanticIndex,
234
+ metadata: {
235
+ ...projectImport.metadata,
236
+ projectSymbolGraph
237
+ }
238
+ };
239
+ }
240
+
218
241
  function hashText(sourceText) {
219
242
  return hashSemanticValue(sourceText);
220
243
  }
@@ -0,0 +1,117 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { compactRecord } from './js-ts-safe-merge-context.js';
3
+ import { findContainer, normalizeKind, normalizeMemberText, parseMembers } from './js-ts-semantic-merge-parse.js';
4
+ import { idFragment } from './native-import-utils.js';
5
+
6
+ function augmentProjectSymbolGraphPublicContracts(projectSymbolGraph, files, input, stageName) {
7
+ const existingRegions = Array.isArray(projectSymbolGraph?.publicContractRegions) ? projectSymbolGraph.publicContractRegions : [];
8
+ const seenKeys = new Set(existingRegions.map((region) => region?.key).filter(Boolean));
9
+ const syntheticRegions = [];
10
+ for (const file of files) {
11
+ syntheticRegions.push(...syntheticPublicContractRegions(file, input, stageName, seenKeys));
12
+ }
13
+ if (!syntheticRegions.length) return projectSymbolGraph;
14
+ return {
15
+ ...projectSymbolGraph,
16
+ publicContractRegions: [...existingRegions, ...syntheticRegions]
17
+ };
18
+ }
19
+
20
+ function syntheticPublicContractRegions(file, input, stageName, seenKeys) {
21
+ if (typeof file?.sourceText !== 'string' || !file.sourcePath) return [];
22
+ const records = [];
23
+ for (const region of publicContractPolicyRegionsForPath(input, file.sourcePath)) {
24
+ const kind = normalizeKind(region?.kind);
25
+ if (!['interface', 'type', 'class'].includes(kind) || typeof region?.name !== 'string') continue;
26
+ const container = findContainer(file.sourceText, region);
27
+ if (container.reasonCodes.length || !isExportedContainer(file.sourceText, container.value)) continue;
28
+ const members = parseMembers(container.value.body, kind);
29
+ if (members.reasonCodes.length) continue;
30
+ const key = `source#${file.sourcePath}#export#${region.name}`;
31
+ if (seenKeys.has(key)) continue;
32
+ seenKeys.add(key);
33
+ records.push(syntheticPublicContractRegion(file, input, stageName, kind, region.name, key, members.members));
34
+ }
35
+ return records;
36
+ }
37
+
38
+ function syntheticPublicContractRegion(file, input, stageName, kind, name, key, members) {
39
+ const language = file.language ?? input.language ?? languageForPath(file.sourcePath);
40
+ const memberRecords = members.map((member) => compactRecord({
41
+ key: member.key,
42
+ memberKind: member.memberKind,
43
+ signature: normalizeMemberText(member.text, kind)
44
+ }));
45
+ const signatureHash = hashSemanticValue({
46
+ kind: 'frontier.lang.syntheticPublicContractSignature',
47
+ language,
48
+ sourcePath: file.sourcePath,
49
+ symbolName: name,
50
+ sourceKind: kind,
51
+ members: memberRecords
52
+ });
53
+ const contractHash = hashSemanticValue({
54
+ kind: 'frontier.lang.syntheticPublicContractRegionHash',
55
+ language,
56
+ sourcePath: file.sourcePath,
57
+ key,
58
+ symbolName: name,
59
+ symbolKind: 'export',
60
+ apiSurfaceKind: 'module-export',
61
+ exportedName: name,
62
+ edgeKind: 'export',
63
+ signatureHash
64
+ });
65
+ return compactRecord({
66
+ id: `region_${idFragment(stageName)}_${idFragment(key)}`,
67
+ key,
68
+ regionKind: 'export',
69
+ granularity: 'symbol',
70
+ language,
71
+ sourcePath: file.sourcePath,
72
+ sourceHash: file.sourceHash,
73
+ symbolId: `symbol:${language}:export:${idFragment(name)}`,
74
+ symbolName: name,
75
+ symbolKind: 'export',
76
+ publicContract: true,
77
+ exportedName: name,
78
+ edgeKind: 'export',
79
+ apiSurfaceKind: 'module-export',
80
+ signatureHash,
81
+ contractHash,
82
+ metadata: {
83
+ source: 'js-ts-safe-project-merge-policy',
84
+ projectGraphStage: stageName,
85
+ sourceKind: kind,
86
+ memberKeys: memberRecords.map((member) => member.key)
87
+ }
88
+ });
89
+ }
90
+
91
+ function publicContractPolicyRegionsForPath(input, sourcePath) {
92
+ const policy = input.policyByPath?.[sourcePath]
93
+ ?? input.mergePolicyByPath?.[sourcePath]
94
+ ?? input.policy
95
+ ?? input.mergePolicy;
96
+ const regions = Array.isArray(policy)
97
+ ? policy
98
+ : policy?.unorderedRegions
99
+ ?? policy?.unorderedMemberRegions
100
+ ?? policy?.safeList
101
+ ?? policy?.safeMembers
102
+ ?? [];
103
+ return Array.isArray(regions) ? regions : [];
104
+ }
105
+
106
+ function isExportedContainer(sourceText, container) {
107
+ if (!container) return false;
108
+ return /^\s*export\b/.test(sourceText.slice(container.start, container.openStart));
109
+ }
110
+
111
+ function languageForPath(sourcePath) {
112
+ const path = String(sourcePath ?? '').toLowerCase();
113
+ if (path.endsWith('.js') || path.endsWith('.jsx') || path.endsWith('.mjs') || path.endsWith('.cjs')) return 'javascript';
114
+ return 'typescript';
115
+ }
116
+
117
+ export { augmentProjectSymbolGraphPublicContracts };
@@ -17,7 +17,7 @@ function removePreparedMemberAdditions(sourceText, preparedRegions, side) {
17
17
  const replacements = preparedRegions
18
18
  .map((region) => ({
19
19
  range: region[side],
20
- replacement: removeMembersFromBody(region[side].body, region[`${side}AddedMembers`] ?? [], region.kind)
20
+ replacement: removeMembersFromBody(region[side].body, region[`${side}AddedMembers`] ?? [], region.kind, region.base.body)
21
21
  }))
22
22
  .sort((left, right) => right.range.bodyStart - left.range.bodyStart);
23
23
  for (const { range, replacement } of replacements) {
@@ -88,16 +88,25 @@ function appendReadyBody(before, kind) {
88
88
  return `${before.replace(/\s*$/, '')},`;
89
89
  }
90
90
 
91
- function removeMembersFromBody(body, members, kind) {
91
+ function removeMembersFromBody(body, members, kind, baseBody) {
92
92
  let output = body;
93
93
  for (const member of [...members].sort((left, right) => right.start - left.start || right.end - left.end)) {
94
94
  output = `${output.slice(0, member.start)}${output.slice(member.end)}`;
95
95
  }
96
96
  if (kind !== 'object') return output;
97
+ if (typeof baseBody === 'string' && sameObjectBodyIgnoringTrailingDelimiter(output, baseBody)) return baseBody;
97
98
  const trailing = String(body ?? '').match(/\s*$/)?.[0] ?? '';
98
99
  return output.replace(/,\s*$/, trailing);
99
100
  }
100
101
 
102
+ function sameObjectBodyIgnoringTrailingDelimiter(left, right) {
103
+ return normalizeObjectTrailingDelimiter(left) === normalizeObjectTrailingDelimiter(right);
104
+ }
105
+
106
+ function normalizeObjectTrailingDelimiter(body) {
107
+ return String(body ?? '').replace(/,\s*$/, (trailing) => trailing.replace(',', ''));
108
+ }
109
+
101
110
  function minimumIndent(lines) {
102
111
  const indents = lines.filter((line) => line.trim()).map(leadingWhitespace);
103
112
  return indents.length ? Math.min(...indents) : 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.144",
3
+ "version": "0.2.146",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",