@shapeshift-labs/frontier-lang-compiler 0.2.116 → 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 +29 -0
- package/dist/declarations/native-project-module-resolution.d.ts +6 -0
- package/dist/declarations/native-project.d.ts +4 -4
- package/dist/internal/index-impl/createNativeProjectImportResult.js +4 -0
- package/dist/internal/index-impl/projectSymbolGraphModulePathCandidates.js +52 -0
- package/dist/internal/index-impl/projectSymbolGraphModuleResolution.js +68 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -238,6 +238,35 @@ 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
|
+
|
|
247
|
+
Package `imports` maps are also modeled for `#internal` specifiers. Top-level
|
|
248
|
+
`moduleResolution.imports` applies from `packageRoot`/`root`, while
|
|
249
|
+
`packages[name].imports` applies to the nearest configured package root. Graph
|
|
250
|
+
edges record `packageImportKey`, `packageImportCondition`, and
|
|
251
|
+
`packageImportTarget` so merge admission can distinguish private aliases from
|
|
252
|
+
external or unresolved imports:
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
const project = safeMergeJsTsProject({
|
|
256
|
+
includeOutputProjectSymbolGraph: true,
|
|
257
|
+
moduleResolution: {
|
|
258
|
+
imports: { '#internal/*': { import: './src/internal/*.ts', default: './src/internal/*.js' } },
|
|
259
|
+
packageExportConditions: ['import', 'default']
|
|
260
|
+
},
|
|
261
|
+
baseFiles,
|
|
262
|
+
workerFiles,
|
|
263
|
+
headFiles
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
console.log(project.outputProjectSymbolGraph.importEdges[0].resolutionKind); // "package-import-source"
|
|
267
|
+
console.log(project.outputProjectSymbolGraph.importEdges[0].packageImportKey); // "#internal/*"
|
|
268
|
+
```
|
|
269
|
+
|
|
241
270
|
Named re-export identities also include symbol links when the project graph has
|
|
242
271
|
enough evidence. For `export { thing as renamedThing } from './thing.js'`,
|
|
243
272
|
`reExportIdentities[]` records the source module, imported/exported names,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type NativeProjectModuleResolutionPaths = Readonly<Record<string, readonly string[] | string>>;
|
|
2
2
|
export type NativeProjectPackageExportTarget = string | readonly string[] | NativeProjectPackageConditionalExports;
|
|
3
3
|
export type NativeProjectPackageExports = NativeProjectPackageExportTarget | Readonly<Record<string, NativeProjectPackageExportTarget>>;
|
|
4
|
+
export type NativeProjectPackageImports = Readonly<Record<string, NativeProjectPackageExportTarget>>;
|
|
4
5
|
|
|
5
6
|
export interface NativeProjectPackageConditionalExports {
|
|
6
7
|
readonly [condition: string]: NativeProjectPackageExportTarget;
|
|
@@ -12,12 +13,17 @@ export interface NativeProjectPackageResolutionOptions {
|
|
|
12
13
|
readonly main?: string;
|
|
13
14
|
readonly types?: string;
|
|
14
15
|
readonly exports?: NativeProjectPackageExports;
|
|
16
|
+
readonly imports?: NativeProjectPackageImports;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface NativeProjectModuleResolutionOptions {
|
|
20
|
+
readonly root?: string;
|
|
21
|
+
readonly packageRoot?: string;
|
|
18
22
|
readonly baseUrl?: string;
|
|
19
23
|
readonly paths?: NativeProjectModuleResolutionPaths;
|
|
20
24
|
readonly aliases?: NativeProjectModuleResolutionPaths;
|
|
25
|
+
readonly imports?: NativeProjectPackageImports;
|
|
26
|
+
readonly packageImports?: NativeProjectPackageImports;
|
|
21
27
|
readonly packages?: Readonly<Record<string, NativeProjectPackageResolutionOptions>>;
|
|
22
28
|
readonly conditions?: readonly string[];
|
|
23
29
|
readonly packageExportConditions?: readonly string[];
|
|
@@ -107,10 +107,10 @@ export interface NativeProjectSymbolGraphModuleEdgeRecord {
|
|
|
107
107
|
readonly resolvedModulePath?: string;
|
|
108
108
|
readonly targetDocumentId?: string;
|
|
109
109
|
readonly resolvedTargetSymbolId?: string;
|
|
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' | string;
|
|
111
|
-
readonly
|
|
112
|
-
readonly packageSubpath?: string;
|
|
113
|
-
readonly
|
|
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;
|
|
112
|
+
readonly packageName?: string; readonly packageSubpath?: string; readonly packageExportCondition?: string;
|
|
113
|
+
readonly packageImportKey?: string; readonly packageImportCondition?: string; readonly packageImportTarget?: string;
|
|
114
114
|
readonly importKind?: string;
|
|
115
115
|
readonly exportKind?: string;
|
|
116
116
|
readonly importedName?: string;
|
|
@@ -240,9 +240,13 @@ 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,
|
|
247
|
+
packageImportKey: resolution?.packageImportKey,
|
|
248
|
+
packageImportCondition: resolution?.packageImportCondition,
|
|
249
|
+
packageImportTarget: resolution?.packageImportTarget,
|
|
246
250
|
importKind: firstString(moduleEdge.importKind, value.importKind, symbolMetadata.importKind),
|
|
247
251
|
exportKind: firstString(moduleEdge.exportKind, value.exportKind, symbolMetadata.exportKind),
|
|
248
252
|
importedName: firstString(moduleEdge.importedName, value.importedName, symbolMetadata.importedName),
|
|
@@ -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
|
}
|
|
@@ -13,6 +16,7 @@ export function resolveRelativeProjectModule(sourcePath, moduleSpecifier, docume
|
|
|
13
16
|
export function resolveProjectModule(sourcePath, moduleSpecifier, documentsByPath, moduleResolution) {
|
|
14
17
|
if (!sourcePath || !moduleSpecifier) return undefined;
|
|
15
18
|
if (String(moduleSpecifier).startsWith('.')) return resolveRelativeProjectModule(sourcePath, moduleSpecifier, documentsByPath);
|
|
19
|
+
if (String(moduleSpecifier).startsWith('#')) return resolvePackageImportProjectModule(sourcePath, moduleSpecifier, documentsByPath, moduleResolution);
|
|
16
20
|
return resolveConfiguredProjectModule(moduleSpecifier, documentsByPath, moduleResolution);
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -68,12 +72,38 @@ function resolveConfiguredProjectModule(moduleSpecifier, documentsByPath, module
|
|
|
68
72
|
for (const candidate of candidates) {
|
|
69
73
|
const target = moduleTargetDocument(candidate.path, documentsByPath);
|
|
70
74
|
const packageFields = packageResolutionFields(candidate, packageInfo);
|
|
71
|
-
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 };
|
|
72
76
|
firstMissing ??= { path: candidate.path, kind: `${candidate.kind}-missing`, ...packageFields };
|
|
73
77
|
}
|
|
74
78
|
return firstMissing ?? (packageInfo ? { kind: 'package-external', ...packageInfo } : undefined);
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
function resolvePackageImportProjectModule(sourcePath, moduleSpecifier, documentsByPath, moduleResolution = {}) {
|
|
82
|
+
const packageContext = packageImportContext(sourcePath, moduleResolution);
|
|
83
|
+
const importsValue = packageContext.imports;
|
|
84
|
+
if (!importsValue) return { kind: 'package-import-external', packageImportKey: moduleSpecifier };
|
|
85
|
+
const match = packageImportMapValue(importsValue, moduleSpecifier);
|
|
86
|
+
if (!match) return { kind: 'package-import-external', packageImportKey: moduleSpecifier };
|
|
87
|
+
let firstMissing;
|
|
88
|
+
for (const target of exportTargetsForValue(match.value, packageConditions(moduleResolution))) {
|
|
89
|
+
const packageImportTarget = target.path;
|
|
90
|
+
if (!packageImportTarget || !String(packageImportTarget).startsWith('.')) {
|
|
91
|
+
return { kind: 'package-import-external', packageImportKey: match.key, packageImportCondition: target.condition, packageImportTarget };
|
|
92
|
+
}
|
|
93
|
+
const candidatePath = normalizeProjectPath(joinProjectPath(packageContext.root, packageImportTarget));
|
|
94
|
+
const resolved = moduleTargetDocument(candidatePath, documentsByPath);
|
|
95
|
+
const record = {
|
|
96
|
+
packageImportKey: match.key,
|
|
97
|
+
packageImportCondition: target.condition,
|
|
98
|
+
packageImportTarget,
|
|
99
|
+
packageName: packageContext.packageName
|
|
100
|
+
};
|
|
101
|
+
if (resolved) return { path: resolved.path, documentId: resolved.id, resolutionPathVariant: resolved.resolutionPathVariant, kind: 'package-import-source', ...record };
|
|
102
|
+
firstMissing ??= { path: candidatePath, kind: 'package-import-missing', ...record };
|
|
103
|
+
}
|
|
104
|
+
return firstMissing ?? { kind: 'package-import-external', packageImportKey: match.key };
|
|
105
|
+
}
|
|
106
|
+
|
|
77
107
|
function configuredModuleCandidates(moduleSpecifier, moduleResolution = {}, packageInfo) {
|
|
78
108
|
const compilerOptions = moduleResolution.compilerOptions ?? {};
|
|
79
109
|
const baseUrl = normalizeProjectPath(moduleResolution.baseUrl ?? compilerOptions.baseUrl ?? '');
|
|
@@ -136,6 +166,31 @@ function packageExportTargets(exportsValue, subpath, conditions) {
|
|
|
136
166
|
return exportTargetsForValue(exportMapValue(exportsValue, subpath), conditions);
|
|
137
167
|
}
|
|
138
168
|
|
|
169
|
+
function packageImportMapValue(importsValue, moduleSpecifier) {
|
|
170
|
+
if (!isRecord(importsValue)) return undefined;
|
|
171
|
+
if (Object.prototype.hasOwnProperty.call(importsValue, moduleSpecifier)) return { key: moduleSpecifier, value: importsValue[moduleSpecifier] };
|
|
172
|
+
for (const [pattern, value] of Object.entries(importsValue)) {
|
|
173
|
+
const capture = patternCapture(moduleSpecifier, pattern);
|
|
174
|
+
if (capture !== undefined) return { key: pattern, value: replaceExportTargetCapture(value, capture) };
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function packageImportContext(sourcePath, moduleResolution = {}) {
|
|
180
|
+
const packages = Object.entries(moduleResolution.packages ?? {})
|
|
181
|
+
.map(([packageName, options]) => ({
|
|
182
|
+
packageName,
|
|
183
|
+
root: normalizeProjectPath(options.root ?? ''),
|
|
184
|
+
imports: options.imports
|
|
185
|
+
}))
|
|
186
|
+
.filter((entry) => entry.imports && pathInsideRoot(sourcePath, entry.root))
|
|
187
|
+
.sort((left, right) => right.root.length - left.root.length);
|
|
188
|
+
return packages[0] ?? {
|
|
189
|
+
root: normalizeProjectPath(moduleResolution.packageRoot ?? moduleResolution.root ?? ''),
|
|
190
|
+
imports: moduleResolution.imports ?? moduleResolution.packageImports
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
139
194
|
function exportMapValue(exportsValue, subpath) {
|
|
140
195
|
if (!isRecord(exportsValue) || !Object.keys(exportsValue).some((key) => key.startsWith('.'))) return subpath === '.' ? exportsValue : undefined;
|
|
141
196
|
return exportsValue[subpath] ?? patternExportMapValue(exportsValue, subpath);
|
|
@@ -172,16 +227,12 @@ function packageFallbackTargets(options, subpath) {
|
|
|
172
227
|
|
|
173
228
|
function moduleTargetDocument(path, documentsByPath) {
|
|
174
229
|
for (const candidate of modulePathCandidates(path)) {
|
|
175
|
-
const document = documentsByPath.get(candidate);
|
|
176
|
-
if (document) return document;
|
|
230
|
+
const document = documentsByPath.get(candidate.path);
|
|
231
|
+
if (document) return { ...document, resolutionPathVariant: candidate.variant };
|
|
177
232
|
}
|
|
178
233
|
return undefined;
|
|
179
234
|
}
|
|
180
235
|
|
|
181
|
-
function modulePathCandidates(path) {
|
|
182
|
-
return [path, `${path}.js`, `${path}.ts`, `${path}.tsx`, `${path}.jsx`, `${path}.d.ts`, `${path}/index.js`, `${path}/index.ts`, `${path}/index.d.ts`];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
236
|
function targetExportName(edge) {
|
|
186
237
|
const name = edge.importedName ?? edge.localName ?? edge.exportedName;
|
|
187
238
|
if (!name || name === '*') return undefined;
|
|
@@ -229,7 +280,10 @@ function packageResolutionFields(candidate, packageInfo) {
|
|
|
229
280
|
return {
|
|
230
281
|
packageName: candidate.packageName ?? packageInfo?.packageName,
|
|
231
282
|
packageSubpath: candidate.packageSubpath ?? packageInfo?.packageSubpath,
|
|
232
|
-
packageExportCondition: candidate.packageExportCondition
|
|
283
|
+
packageExportCondition: candidate.packageExportCondition,
|
|
284
|
+
packageImportKey: candidate.packageImportKey,
|
|
285
|
+
packageImportCondition: candidate.packageImportCondition,
|
|
286
|
+
packageImportTarget: candidate.packageImportTarget
|
|
233
287
|
};
|
|
234
288
|
}
|
|
235
289
|
|
|
@@ -250,3 +304,9 @@ function normalizeProjectPath(path) {
|
|
|
250
304
|
}
|
|
251
305
|
return parts.join('/');
|
|
252
306
|
}
|
|
307
|
+
|
|
308
|
+
function pathInsideRoot(sourcePath, root) {
|
|
309
|
+
if (!root) return true;
|
|
310
|
+
const normalized = normalizeProjectPath(sourcePath);
|
|
311
|
+
return normalized === root || normalized.startsWith(`${root}/`);
|
|
312
|
+
}
|
package/package.json
CHANGED