From 53409ab1968d60c77f98feedefe237a08859a95c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 8 Oct 2026 12:45:21 +0800 Subject: [PATCH] fix(javascript): resolve root package exports arrays (#1046) Co-authored-by: N0zoM1z0 <1705004398@qq.com> --- docs/javascript-artifact-reconstruction.md | 4 ++++ .../JavaScriptArtifactPathResolution.ts | 6 ++---- .../javascriptPackagePrecedence.test.ts | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/javascript-artifact-reconstruction.md b/docs/javascript-artifact-reconstruction.md index ccca4d12f..52038d16d 100644 --- a/docs/javascript-artifact-reconstruction.md +++ b/docs/javascript-artifact-reconstruction.md @@ -226,3 +226,7 @@ URI schemes are classified independently of local file names: a reference such as `web3:app.js` remains external even when an artifact has that literal name. Scheme characters may include digits after the initial letter; a relative path such as `./web3:app.js` still names a local artifact. + +Package `exports` fallback arrays are supported both at the top level and under +the root `"."` entry. The resolver selects targets in declared order using the +same conditional and invalid-entry handling as nested exports arrays. diff --git a/src/application/javascript/JavaScriptArtifactPathResolution.ts b/src/application/javascript/JavaScriptArtifactPathResolution.ts index 3450ffd20..956da69e5 100644 --- a/src/application/javascript/JavaScriptArtifactPathResolution.ts +++ b/src/application/javascript/JavaScriptArtifactPathResolution.ts @@ -440,12 +440,10 @@ const packageExport = ( moduleKind: ResolveArtifactPathInput["moduleKind"], ): PackageExportOutcome => { if (typeof value === "string") return packagePathValue(value); - if (typeof value !== "object" || value === null || Array.isArray(value)) - return { status: "invalid" }; + if (typeof value !== "object" || value === null) return { status: "invalid" }; const root = Reflect.get(value, ".") ?? value; if (typeof root === "string") return packagePathValue(root); - if (typeof root !== "object" || root === null || Array.isArray(root)) - return { status: "invalid" }; + if (typeof root !== "object" || root === null) return { status: "invalid" }; const flattened = exportTargets(root, packageExportConditions(moduleKind)); if (flattened.kind === "invalid") return { status: "invalid" }; const first = diff --git a/tests/boundary/filesystem/javascriptPackagePrecedence.test.ts b/tests/boundary/filesystem/javascriptPackagePrecedence.test.ts index 113f92593..14c056166 100644 --- a/tests/boundary/filesystem/javascriptPackagePrecedence.test.ts +++ b/tests/boundary/filesystem/javascriptPackagePrecedence.test.ts @@ -25,6 +25,26 @@ const cases: readonly { metadata: { main: "actual.cjs" }, index: false, }, + { + name: "top-level exports array", + metadata: { exports: ["./actual.cjs"] }, + index: true, + }, + { + name: "root subpath exports array", + metadata: { exports: { ".": ["./actual.cjs"] } }, + index: true, + }, + { + name: "exports array skips invalid and unmatched entries", + metadata: { exports: [42, { browser: "./browser.js" }, "./actual.cjs"] }, + index: true, + }, + { + name: "nested root exports array", + metadata: { exports: { ".": [[null, { require: "./actual.cjs" }]] } }, + index: true, + }, { name: "index fallback", metadata: {}, index: true }, { name: "missing legacy main fallback",