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

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
@@ -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,
@@ -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;
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.118",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",