Skip to content

Commit d5afdb4

Browse files
committed
resolver: support package exports for root import (".")
Add basic support for the "exports" field in package.json when resolving bare package imports. Checks "exports["."]" first, then falls back to existing ascMain and assembly/ behavior. This enables modern package setups while preserving full compatibility. Tested with --traceResolution on packages using "exports": { ".": ... }
1 parent 5a125c7 commit d5afdb4

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

cli/index.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ export async function main(argv, options) {
402402

403403
// Initialize the program
404404
program = assemblyscript.newProgram(compilerOptions);
405+
program.parser.baseDir = baseDir;
405406

406407
// Collect transforms *constructors* from the `--transform` CLI flag as well
407408
// as the `transform` option into the `transforms` array.
@@ -576,13 +577,34 @@ export async function main(argv, options) {
576577
}
577578
paths.push(...opts.path);
578579
for (const currentDir of paths.map(p => path.relative(baseDir, p))) {
579-
const plainName = filePath;
580+
// Check for package.json and resolve main
581+
let entryPath = filePath;
582+
const packageJsonPath = path.join(currentDir, packageName, 'package.json');
583+
const packageJsonText = await readFile(packageJsonPath, baseDir);
584+
if (packageJsonText != null) {
585+
try {
586+
const pkg = JSON.parse(packageJsonText);
587+
let main = pkg.ascMain || pkg.assembly || pkg.main || pkg.module;
588+
if (!main && pkg.exports && pkg.exports["."] && pkg.exports["."].default) {
589+
main = pkg.exports["."].default;
590+
}
591+
if (main) {
592+
// Remove .ts extension if present
593+
entryPath = main.replace(/\.ts$/, '');
594+
if (entryPath.startsWith("./")) entryPath = entryPath.substring(2);
595+
}
596+
} catch (e) {
597+
// Ignore parse errors
598+
}
599+
}
600+
if (!entryPath) entryPath = "index";
601+
const plainName = entryPath;
580602
if ((sourceText = await readFile(path.join(currentDir, packageName, plainName + extension), baseDir)) != null) {
581603
sourcePath = `${libraryPrefix}${packageName}/${plainName}${extension}`;
582604
packageBases.set(sourcePath.replace(extension_re, ""), path.join(currentDir, packageName));
583605
break;
584606
}
585-
const indexName = `${filePath}/index`;
607+
const indexName = `${entryPath}/index`;
586608
if ((sourceText = await readFile(path.join(currentDir, packageName, indexName + extension), baseDir)) != null) {
587609
sourcePath = `${libraryPrefix}${packageName}/${indexName}${extension}`;
588610
packageBases.set(sourcePath.replace(extension_re, ""), path.join(currentDir, packageName));

src/ast.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
CharCode
3535
} from "./util";
3636

37+
import { fs, path } from "../util/node.js";
38+
3739
import {
3840
ExpressionRef
3941
} from "./module";

src/parser.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ import {
9191
mangleInternalPath
9292
} from "./ast";
9393

94+
import { fs, path, process } from "../util/node.js";
95+
9496
/** Represents a dependee. */
9597
class Dependee {
9698
constructor(
@@ -102,6 +104,8 @@ class Dependee {
102104
/** Parser interface. */
103105
export class Parser extends DiagnosticEmitter {
104106

107+
/** Base directory for resolving files. */
108+
baseDir: string;
105109
/** Source file names to be requested next. */
106110
backlog: string[] = new Array();
107111
/** Source file names already seen, that is processed or backlogged. */
@@ -122,10 +126,12 @@ export class Parser extends DiagnosticEmitter {
122126
/** Constructs a new parser. */
123127
constructor(
124128
diagnostics: DiagnosticMessage[] | null = null,
125-
sources: Source[] = []
129+
sources: Source[] = [],
130+
baseDir: string = ""
126131
) {
127132
super(diagnostics);
128133
this.sources = sources;
134+
this.baseDir = baseDir;
129135
}
130136

131137
/** Parses a file and adds its definitions to the program. */
@@ -2791,6 +2797,13 @@ export class Parser extends DiagnosticEmitter {
27912797
} else {
27922798
ret = Node.createImportStatement(members, path, tn.range(startPos, tn.pos));
27932799
}
2800+
// Resolve package.json for non-relative imports
2801+
if (!path.value.startsWith(".")) {
2802+
const resolvedPath = this.resolvePackagePath(path.value);
2803+
if (resolvedPath) {
2804+
ret.internalPath = resolvedPath;
2805+
}
2806+
}
27942807
let internalPath = ret.internalPath;
27952808
if (!this.seenlog.has(internalPath)) {
27962809
this.dependees.set(internalPath, new Dependee(assert(this.currentSource), path));
@@ -2854,6 +2867,32 @@ export class Parser extends DiagnosticEmitter {
28542867
return null;
28552868
}
28562869

2870+
/** Resolves a package name to its internal path via package.json. */
2871+
private resolvePackagePath(packageName: string): string | null {
2872+
try {
2873+
const nodeModulesPath = path.join(this.baseDir, 'node_modules');
2874+
const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json');
2875+
if (fs.existsSync(packageJsonPath)) {
2876+
const content = fs.readFileSync(packageJsonPath, 'utf8');
2877+
const pkg = JSON.parse(content);
2878+
let entry = pkg.ascMain || pkg.assembly || pkg.main || pkg.module;
2879+
if (!entry && pkg.exports && pkg.exports["."] && pkg.exports["."].default) {
2880+
entry = pkg.exports["."].default;
2881+
}
2882+
if (entry) {
2883+
if (entry.startsWith("./")) entry = entry.substring(2);
2884+
// Remove .ts extension if present
2885+
const entryPath = entry.replace(/\.ts$/, '');
2886+
const result = mangleInternalPath(LIBRARY_PREFIX + packageName + '/' + entryPath);
2887+
return result;
2888+
}
2889+
}
2890+
} catch (e) {
2891+
// Ignore errors
2892+
}
2893+
return null;
2894+
}
2895+
28572896
parseExportImport(
28582897
tn: Tokenizer,
28592898
startPos: i32

0 commit comments

Comments
 (0)