Let bundlers resolve the published @getpaseo/relay package (#5815)

The relay exports map sent the import and default conditions to src/*.ts,
which the published package does not include, so any bundler resolving
@getpaseo/client failed with 'Could not resolve "@getpaseo/relay/e2ee"'.
Every condition now resolves to dist, like @getpaseo/protocol and
@getpaseo/client. The client build now builds relay first, and the Metro
workaround for compiling relay source is removed.

Fixes #4730

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-10-01 05:01:45 +02:00
committed by GitHub
parent b701cb2dcd
commit 15f1431416
4 changed files with 53 additions and 21 deletions
-11
View File
@@ -1,12 +1,10 @@
const { getDefaultConfig } = require("expo/metro-config");
const { resolve } = require("metro-resolver");
const fs = require("fs");
const path = require("path");
const projectRoot = __dirname;
const appNodeModulesRoot = path.resolve(projectRoot, "node_modules");
const appSrcRoot = path.resolve(projectRoot, "src");
const relaySrcRoot = path.resolve(projectRoot, "../relay/src");
const isFdroidBuild = process.env.PASEO_FDROID_BUILD === "1";
const fdroidModuleOverrides = {
"expo-camera": path.resolve(appSrcRoot, "fdroid/expo-camera.tsx"),
@@ -81,15 +79,6 @@ config.resolver.resolveRequest = (context, moduleName, platform) => {
return resolveWithCustomWebOverlay(context, fdroidModuleOverrides[moduleName], platform);
}
const origin = context.originModulePath;
if (origin && origin.startsWith(relaySrcRoot) && moduleName.endsWith(".js")) {
const tsModuleName = moduleName.replace(/\.js$/, ".ts");
const candidatePath = path.resolve(path.dirname(origin), tsModuleName);
if (fs.existsSync(candidatePath)) {
return resolveWithCustomWebOverlay(context, tsModuleName, platform);
}
}
return resolveWithCustomWebOverlay(context, moduleName, platform);
};
+1 -1
View File
@@ -31,7 +31,7 @@
},
"scripts": {
"clean": "node ../../scripts/clean-package-dist.mjs",
"build": "npm run build --workspace=@getpaseo/protocol && tsc -p tsconfig.json --incremental false",
"build": "npm run build --workspace=@getpaseo/protocol && npm run build --workspace=@getpaseo/relay && tsc -p tsconfig.json --incremental false",
"build:clean": "npm run clean && npm run build",
"prepack": "npm run build:clean",
"typecheck": "tsgo --noEmit",
+3 -9
View File
@@ -11,21 +11,15 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": "./dist/index.js",
"import": "./src/index.ts",
"default": "./src/index.ts"
"default": "./dist/index.js"
},
"./e2ee": {
"types": "./dist/e2ee.d.ts",
"node": "./dist/e2ee.js",
"import": "./src/e2ee.ts",
"default": "./src/e2ee.ts"
"default": "./dist/e2ee.js"
},
"./cloudflare": {
"types": "./dist/cloudflare-adapter.d.ts",
"node": "./dist/cloudflare-adapter.js",
"import": "./src/cloudflare-adapter.ts",
"default": "./src/cloudflare-adapter.ts"
"default": "./dist/cloudflare-adapter.js"
}
},
"publishConfig": {
@@ -0,0 +1,49 @@
import { existsSync, readFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const THIS_DIR = path.dirname(fileURLToPath(import.meta.url));
const RELAY_PACKAGE_ROOT = path.resolve(THIS_DIR, "..");
interface PackageManifest {
exports: Record<string, Record<string, string>>;
}
function readManifest(): PackageManifest {
return JSON.parse(readFileSync(path.join(RELAY_PACKAGE_ROOT, "package.json"), "utf8"));
}
function listPublishedFiles(): Set<string> {
if (!existsSync(path.join(RELAY_PACKAGE_ROOT, "dist/index.js"))) {
execFileSync("npm", ["run", "build"], { cwd: RELAY_PACKAGE_ROOT, stdio: "inherit" });
}
const output = execFileSync("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], {
cwd: RELAY_PACKAGE_ROOT,
encoding: "utf8",
});
const [pack] = JSON.parse(output) as Array<{ files: Array<{ path: string }> }>;
return new Set(pack.files.map((file) => file.path));
}
function listExportTargets(manifest: PackageManifest): Array<{ label: string; file: string }> {
const targets: Array<{ label: string; file: string }> = [];
for (const [subpath, conditions] of Object.entries(manifest.exports)) {
for (const [condition, target] of Object.entries(conditions)) {
targets.push({ label: `${subpath} [${condition}] -> ${target}`, file: target.slice(2) });
}
}
return targets;
}
describe("published relay package", () => {
it("ships every file its exports map resolves to, for every condition", () => {
const published = listPublishedFiles();
const missing = listExportTargets(readManifest())
.filter((target) => !published.has(target.file))
.map((target) => target.label);
expect(missing).toEqual([]);
});
});