diff --git a/packages/natives/native/loader-state.d.ts b/packages/natives/native/loader-state.d.ts index d6fe483be..f62553855 100644 --- a/packages/natives/native/loader-state.d.ts +++ b/packages/natives/native/loader-state.d.ts @@ -55,6 +55,31 @@ export interface ResolveLoaderCandidatesInput { export function resolveLoaderCandidates(input: ResolveLoaderCandidatesInput): string[]; +export interface InitLoaderContextOverrides { + nativeDir?: string; + isCompiledBinary?: boolean; + leafPackageDir?: string | null; +} + +export interface NativeLoaderContext { + platformTag: string; + packageVersion: string; + nativeDir: string; + leafPackageDir: string | null; + versionedDir: string; + isCompiledBinary: boolean; + stageFromNodeModules: boolean; + selectedVariant: "modern" | "baseline" | null; + addonFilenames: string[]; + addonLabel: string; + candidates: string[]; + versionSentinelExport: string; + isWorkspaceLoad: boolean; + nativesDir: string; +} + +export function initLoaderContext(overrides?: InitLoaderContextOverrides): NativeLoaderContext; + export interface CleanupStaleNativeVersionsInput { nativesDir: string; currentVersion: string; diff --git a/packages/natives/native/loader-state.js b/packages/natives/native/loader-state.js index 1ce76b984..9ae2efdd9 100644 --- a/packages/natives/native/loader-state.js +++ b/packages/natives/native/loader-state.js @@ -698,10 +698,13 @@ function buildHelpMessage(ctx) { * Called from `loadNative()` rather than at module scope so importing pure * helpers from this file doesn't trigger AVX2 detection or filesystem probes. */ -function initLoaderContext() { +/** + * @param {{ nativeDir?: string; isCompiledBinary?: boolean; leafPackageDir?: string | null }} [overrides] + */ +export function initLoaderContext(overrides = {}) { const platformTag = `${process.platform}-${process.arch}`; const packageVersion = packageJson.version; - const nativeDir = path.join(import.meta.dir, "..", "native"); + const nativeDir = overrides.nativeDir ?? path.join(import.meta.dir, "..", "native"); const execDir = path.dirname(process.execPath); const nativesDir = getNativesDir(); const versionedDir = path.join(nativesDir, packageVersion); @@ -710,14 +713,21 @@ function initLoaderContext() { ? path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "omp") : path.join(os.homedir(), ".local", "bin"); - const isCompiledBinary = detectCompiledBinary({ - embeddedAddon, - env: process.env, - importMetaUrl: import.meta.url, - }); + const isCompiledBinary = + overrides.isCompiledBinary ?? + detectCompiledBinary({ + embeddedAddon, + env: process.env, + importMetaUrl: import.meta.url, + }); const isWorkspaceLoad = !isCompiledBinary && !nativeDir.includes("\\node_modules\\") && !nativeDir.includes("/node_modules/"); - const leafPackageDir = isCompiledBinary || isWorkspaceLoad ? null : resolveLeafPackageDir(platformTag); + const leafPackageDir = + isCompiledBinary || isWorkspaceLoad + ? null + : overrides.leafPackageDir === undefined + ? resolveLeafPackageDir(platformTag) + : overrides.leafPackageDir; const stageFromNodeModules = shouldStageNodeModulesAddon({ platform: process.platform, isCompiledBinary, diff --git a/packages/natives/test/windows-staging.test.ts b/packages/natives/test/windows-staging.test.ts index fbb577e1b..d30757507 100644 --- a/packages/natives/test/windows-staging.test.ts +++ b/packages/natives/test/windows-staging.test.ts @@ -26,6 +26,7 @@ import * as path from "node:path"; import { cleanupStaleNativeVersions, getAddonFilenames, + initLoaderContext, resolveLoaderCandidates, shouldStageNodeModulesAddon, } from "../native/loader-state.js"; @@ -114,6 +115,28 @@ describe("windows native addon staging", () => { expect(candidates).not.toContain(userDataBaseline); }); + it("omits leaf-package candidates only for workspace loads", () => { + const leafPackageDir = "/tmp/node_modules/@oh-my-pi/pi-natives-darwin-arm64"; + const workspace = initLoaderContext({ + isCompiledBinary: false, + nativeDir: "/tmp/oh-my-pi/packages/natives/native", + leafPackageDir, + }); + const installed = initLoaderContext({ + isCompiledBinary: false, + nativeDir: "/tmp/node_modules/@oh-my-pi/pi-natives/native", + leafPackageDir, + }); + const leafCandidate = path.join(leafPackageDir, workspace.addonFilenames[0]); + + expect(workspace.isWorkspaceLoad).toBe(true); + expect(workspace.leafPackageDir).toBeNull(); + expect(workspace.candidates).not.toContain(leafCandidate); + expect(installed.isWorkspaceLoad).toBe(false); + expect(installed.leafPackageDir).toBe(leafPackageDir); + expect(installed.candidates).toContain(leafCandidate); + }); + it("falls back to the node_modules-only candidate list when staging is off", () => { // Mirrors the non-Windows / workspace-dev path: same behavior as before // the staging feature was introduced.