Skip to content

Commit f17ebc9

Browse files
Guard package export entrypoints
1 parent 695b08b commit f17ebc9

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

scripts/package-runtime-coverage.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,11 @@ test("validatePackageRuntimeCoverage requires root export type conditions", asyn
261261

262262
const rootPackageJson = path.join(tempDir, "package.json");
263263
const packageDir = path.join(tempDir, "packages", "sdk");
264+
const cachePackageDir = path.join(tempDir, "packages", "cache");
264265
await mkdir(packageDir, { recursive: true });
266+
await mkdir(cachePackageDir, { recursive: true });
265267
const sdkPackageJson = path.join(packageDir, "package.json");
268+
const cachePackageJson = path.join(cachePackageDir, "package.json");
266269
await writeFile(
267270
rootPackageJson,
268271
JSON.stringify(
@@ -299,10 +302,27 @@ test("validatePackageRuntimeCoverage requires root export type conditions", asyn
299302
2,
300303
),
301304
);
305+
await writeFile(
306+
cachePackageJson,
307+
JSON.stringify(
308+
{
309+
name: "@ray/cache-test",
310+
type: "module",
311+
main: "./dist/index.js",
312+
exports: {
313+
".": {
314+
default: "./dist/cache.js",
315+
},
316+
},
317+
},
318+
null,
319+
2,
320+
),
321+
);
302322

303323
const summary = await validatePackageRuntimeCoverage({
304324
cwd: tempDir,
305-
packageJsonPaths: [rootPackageJson, sdkPackageJson],
325+
packageJsonPaths: [rootPackageJson, sdkPackageJson, cachePackageJson],
306326
});
307327
const diagnostics = summary.results.flatMap((result) => result.diagnostics);
308328

@@ -321,6 +341,13 @@ test("validatePackageRuntimeCoverage requires root export type conditions", asyn
321341
diagnostic.packagePath === sdkPackageJson,
322342
),
323343
);
344+
assert.ok(
345+
diagnostics.some(
346+
(diagnostic) =>
347+
diagnostic.code === "package_root_export_default_mismatch" &&
348+
diagnostic.packagePath === cachePackageJson,
349+
),
350+
);
324351
});
325352

326353
test("validatePackageRuntimeCoverage requires config and Bun cache storage preflight coverage", async (t) => {

scripts/package-runtime-coverage.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,55 @@ function validatePackageExportTypes(
758758
return diagnostics;
759759
}
760760

761+
function validatePackageExportMain(
762+
packageJsonPath: string,
763+
parsedPackage: Record<string, unknown>,
764+
): PackageRuntimeCoverageDiagnostic[] {
765+
const main = parsedPackage.main;
766+
const exports = parsedPackage.exports;
767+
768+
if (typeof main !== "string" || exports === undefined) {
769+
return [];
770+
}
771+
772+
const diagnostics: PackageRuntimeCoverageDiagnostic[] = [];
773+
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
774+
diagnostics.push({
775+
level: "error",
776+
code: "package_exports_must_be_object_for_main",
777+
packagePath: packageJsonPath,
778+
message:
779+
"Package manifests with top-level main must use an object exports map so JavaScript entrypoints can be checked.",
780+
});
781+
return diagnostics;
782+
}
783+
784+
const rootExport = (exports as Record<string, unknown>)["."];
785+
if (typeof rootExport !== "object" || rootExport === null || Array.isArray(rootExport)) {
786+
diagnostics.push({
787+
level: "error",
788+
code: "package_root_export_must_be_object_for_main",
789+
packagePath: packageJsonPath,
790+
message:
791+
'Package manifests with top-level main must expose exports["."] as an object with a matching default condition.',
792+
});
793+
return diagnostics;
794+
}
795+
796+
const rootExportDefault = (rootExport as Record<string, unknown>).default;
797+
if (rootExportDefault !== main) {
798+
diagnostics.push({
799+
level: "error",
800+
code: "package_root_export_default_mismatch",
801+
packagePath: packageJsonPath,
802+
message:
803+
'Package manifests with top-level main must set exports["."].default to the same JavaScript entrypoint so consumers resolve package exports consistently.',
804+
});
805+
}
806+
807+
return diagnostics;
808+
}
809+
761810
function isLocalHealthCurl(line: string): boolean {
762811
return (
763812
/\bcurl\b/.test(line) &&
@@ -2940,6 +2989,7 @@ export async function validatePackageRuntimeCoverage(
29402989
const diagnostics = [
29412990
...validateRootPackageManager(cwd, packageJsonPath, parsedPackage),
29422991
...validatePackageExportTypes(packageJsonPath, parsedPackage),
2992+
...validatePackageExportMain(packageJsonPath, parsedPackage),
29432993
...validateScripts(packageJsonPath, scripts),
29442994
];
29452995

0 commit comments

Comments
 (0)