test(repo): execute packed subpaths

This commit is contained in:
James
2026-07-18 18:27:44 -04:00
parent d7933aa5fa
commit 3454a80eb2
11 changed files with 135 additions and 42 deletions
+10 -10
View File
@@ -6,7 +6,7 @@
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./package.json": {
"source": "./package.json",
@@ -66,13 +66,13 @@
"source": "./src/lint/index.ts",
"runtime": "./dist/lint/index.js",
"types": "./dist/lint/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./compiler": {
"source": "./src/compiler/index.ts",
"runtime": "./dist/compiler/index.js",
"types": "./dist/compiler/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./color-grading": {
"source": "./src/colorGrading.ts",
@@ -156,25 +156,25 @@
"source": "./src/studio-api/index.ts",
"runtime": "./dist/studio-api/index.js",
"types": "./dist/studio-api/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./studio-api/screenshot-clip": {
"source": "./src/studio-api/helpers/screenshotClip.ts",
"runtime": "./dist/studio-api/helpers/screenshotClip.js",
"types": "./dist/studio-api/helpers/screenshotClip.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./studio-api/manual-edits-render-script": {
"source": "./src/studio-api/helpers/manualEditsRenderScript.ts",
"runtime": "./dist/studio-api/helpers/manualEditsRenderScript.js",
"types": "./dist/studio-api/helpers/manualEditsRenderScript.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./studio-api/studio-motion-render-script": {
"source": "./src/studio-api/helpers/studioMotionRenderScript.ts",
"runtime": "./dist/studio-api/helpers/studioMotionRenderScript.js",
"types": "./dist/studio-api/helpers/studioMotionRenderScript.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./studio-api/draft-markers": {
"source": "./src/studio-api/helpers/draftMarkers.ts",
@@ -186,7 +186,7 @@
"source": "./src/studio-api/helpers/finiteMutation.ts",
"runtime": "./dist/studio-api/helpers/finiteMutation.js",
"types": "./dist/studio-api/helpers/finiteMutation.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./text": {
"source": "./src/text/index.ts",
@@ -252,13 +252,13 @@
"source": "./src/fonts/systemFontLocator.ts",
"runtime": "./dist/fonts/systemFontLocator.js",
"types": "./dist/fonts/systemFontLocator.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./figma": {
"source": "./src/figma/index.ts",
"runtime": "./dist/figma/index.js",
"types": "./dist/figma/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./schemas/registry.json": {
"source": "./schemas/registry.json",
+2 -2
View File
@@ -5,13 +5,13 @@
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./alpha-blit": {
"source": "./src/utils/alphaBlit.ts",
"runtime": "./dist/utils/alphaBlit.js",
"types": "./dist/utils/alphaBlit.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./shader-transitions": {
"source": "./src/utils/shaderTransitions.ts",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./browser": {
"source": "./src/browser.ts",
+1 -1
View File
@@ -66,7 +66,7 @@
"source": "./src/assets.ts",
"runtime": "./dist/assets.js",
"types": "./dist/assets.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./composition": {
"source": "./src/composition.ts",
+2 -2
View File
@@ -9,7 +9,7 @@
"require": "./dist/hyperframes-player.cjs"
},
"types": "./dist/hyperframes-player.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["browser"]
},
"./slideshow": {
"source": "./dist/slideshow/hyperframes-slideshow.js",
@@ -19,7 +19,7 @@
"require": "./dist/slideshow/hyperframes-slideshow.cjs"
},
"types": "./dist/slideshow/hyperframes-slideshow.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["browser"]
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@
"source": "./src/adapters/fs.ts",
"runtime": "./dist/adapters/fs.js",
"types": "./dist/adapters/fs.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./adapters/headless": {
"source": "./src/adapters/headless.ts",
+6 -6
View File
@@ -6,31 +6,31 @@
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./package.json": {
"source": "./package.json",
"runtime": "./package.json",
"types": null,
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./screenshot-clip": {
"source": "./src/helpers/screenshotClip.ts",
"runtime": "./dist/helpers/screenshotClip.js",
"types": "./dist/helpers/screenshotClip.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./manual-edits-render-script": {
"source": "./src/helpers/manualEditsRenderScript.ts",
"runtime": "./dist/helpers/manualEditsRenderScript.js",
"types": "./dist/helpers/manualEditsRenderScript.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./studio-motion-render-script": {
"source": "./src/helpers/studioMotionRenderScript.ts",
"runtime": "./dist/helpers/studioMotionRenderScript.js",
"types": "./dist/helpers/studioMotionRenderScript.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./draft-markers": {
"source": "./src/helpers/draftMarkers.ts",
@@ -42,7 +42,7 @@
"source": "./src/helpers/finiteMutation.ts",
"runtime": "./dist/helpers/finiteMutation.js",
"types": "./dist/helpers/finiteMutation.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./source-mutation": {
"source": "./src/helpers/sourceMutation.ts",
+2 -2
View File
@@ -5,13 +5,13 @@
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["browser"]
},
"./tailwind-preset": {
"source": "./src/styles/tailwind-preset.ts",
"runtime": "./dist/styles/tailwind-preset.js",
"types": "./dist/styles/tailwind-preset.d.ts",
"environments": ["browser", "bun", "node"]
"environments": ["bun", "node"]
},
"./package.json": {
"source": "./package.json",
+1
View File
@@ -16,6 +16,7 @@
"types": "./src/index.ts",
"exports": {
".": {
"browser": "./src/index.ts",
"bun": "./src/index.ts",
"import": "./src/index.ts",
"types": "./src/index.ts"
+64 -15
View File
@@ -1,4 +1,7 @@
#!/usr/bin/env node
// The verifier normalizes conditional exports and materializes multi-runtime
// fixtures; its branch matrix is covered by focused tests plus the live pack gate.
// fallow-ignore-file complexity
import { execFileSync } from "node:child_process";
import {
@@ -298,12 +301,32 @@ export function packageExportSpecifier(packageName, exportKey) {
}
export function listPackedExportContracts(packedWorkspaces) {
return packedWorkspaces.flatMap(({ packedPackage }) =>
Object.entries(packedPackage.exports ?? {}).map(([exportKey, target]) => ({
return packedWorkspaces.flatMap(({ workspace, packedPackage, descriptor }) => {
const sourceDescriptor =
descriptor ??
(workspace && existsSync(join(ROOT, workspace, "package-subpaths.json"))
? JSON.parse(readFileSync(join(ROOT, workspace, "package-subpaths.json"), "utf8"))
: null);
return Object.entries(packedPackage.exports ?? {}).map(([exportKey, target]) => ({
specifier: packageExportSpecifier(packedPackage.name, exportKey),
typechecked: Boolean(target?.types),
})),
environments: sourceDescriptor?.subpaths?.[exportKey]?.environments ?? ["browser", "node"],
}));
});
}
/** Render browser imports as live namespace bindings so sideEffects:false cannot erase the gate. */
export function renderBrowserConsumer(specifiers) {
const bindings = specifiers.map((_, index) => `packedBrowserModule${index}`);
const imports = specifiers.map(
(specifier, index) => `import * as ${bindings[index]} from ${JSON.stringify(specifier)};`,
);
return [
...imports,
`const packedBrowserModules = [${bindings.join(", ")}];`,
`console.log("Packed browser exports", packedBrowserModules.map((module) => Object.keys(module)));`,
"",
].join("\n");
}
function writeConsumerFixture(packDir, packedWorkspaces) {
@@ -314,8 +337,19 @@ function writeConsumerFixture(packDir, packedWorkspaces) {
packedWorkspaces.map(({ filename, packedPackage }) => [packedPackage.name, `file:${filename}`]),
);
const dependencies = { ...workspaceFileDeps };
// Execute optional integration subpaths (for example aws-lambda/cdk) with
// their declared peer contract satisfied, exactly as an adopter would.
for (const { packedPackage } of packedWorkspaces) {
for (const [peer, version] of Object.entries(packedPackage.peerDependencies ?? {})) {
dependencies[peer] ??= version;
}
}
dependencies.typescript = rootPackage.devDependencies.typescript;
dependencies["@types/node"] = rootPackage.devDependencies["@types/node"];
const studioPackage = JSON.parse(
readFileSync(join(ROOT, "packages/studio/package.json"), "utf8"),
);
dependencies.vite = studioPackage.devDependencies.vite;
writeFileSync(
join(fixtureDir, "package.json"),
JSON.stringify(
@@ -363,21 +397,29 @@ function writeConsumerFixture(packDir, packedWorkspaces) {
);
const specifiers = contracts.map(({ specifier }) => specifier);
const nodeSpecifiers = contracts
.filter(({ environments }) => environments.includes("node"))
.map(({ specifier }) => specifier);
const browserSpecifiers = contracts
.filter(({ environments, typechecked }) => environments.includes("browser") && typechecked)
.map(({ specifier }) => specifier);
writeFileSync(
join(fixtureDir, "consumer-smoke.mjs"),
`const specifiers = ${JSON.stringify(specifiers, null, 2)};\n` +
`const nodeSpecifiers = ${JSON.stringify(nodeSpecifiers, null, 2)};\n` +
`for (const specifier of specifiers) import.meta.resolve(specifier);\n` +
`const sdk = await import("@hyperframes/sdk");\n` +
`if (typeof sdk.openComposition !== "function") throw new Error("SDK root export missing");\n` +
`const memory = await import("@hyperframes/sdk/adapters/memory");\n` +
`if (typeof memory.createMemoryAdapter !== "function") throw new Error("memory adapter missing");\n` +
`const fsAdapter = await import("@hyperframes/sdk/adapters/fs");\n` +
`if (typeof fsAdapter.createFsAdapter !== "function") throw new Error("fs adapter missing");\n` +
`await import("@hyperframes/core");\n` +
`await import("@hyperframes/parsers");\n` +
`await import("@hyperframes/lint");\n` +
`await import("@hyperframes/studio-server");\n` +
`console.log(\`Resolved \${specifiers.length} packed exports and loaded public SDK adapters.\`);\n`,
`for (const specifier of nodeSpecifiers) {\n` +
` const options = specifier.endsWith(".json") ? { with: { type: "json" } } : undefined;\n` +
` await import(specifier, options);\n` +
`}\n` +
`console.log(\`Resolved \${specifiers.length} packed exports and executed \${nodeSpecifiers.length} Node exports.\`);\n` +
`process.exit(0);\n`,
);
writeFileSync(join(fixtureDir, "browser-consumer.ts"), renderBrowserConsumer(browserSpecifiers));
writeFileSync(
join(fixtureDir, "vite.config.mjs"),
`import { defineConfig } from "vite";\n` +
`export default defineConfig({ build: { lib: { entry: "browser-consumer.ts", formats: ["es"] }, outDir: "browser-dist" } });\n`,
);
return fixtureDir;
}
@@ -398,6 +440,11 @@ function verifyPackedConsumer(packDir, packedWorkspaces) {
cwd: fixtureDir,
encoding: "utf8",
});
execFileSync(join(fixtureDir, "node_modules", ".bin", "vite"), ["build"], {
cwd: fixtureDir,
encoding: "utf8",
stdio: "pipe",
});
const cliOutput = execFileSync(
join(fixtureDir, "node_modules", ".bin", "hyperframes"),
["--help"],
@@ -411,7 +458,9 @@ function verifyPackedConsumer(packDir, packedWorkspaces) {
throw new Error("Packed CLI help did not identify HyperFrames");
}
console.log(smokeOutput.trim());
console.log("Verified clean packed consumer install, type resolution, and CLI startup.");
console.log(
"Verified clean packed consumer install, Node execution, TypeScript/Vite resolution, and CLI startup.",
);
}
function packAndVerifyWorkspace(workspace, packDir) {
+45 -2
View File
@@ -8,6 +8,7 @@ import {
listPackedExportContracts,
listPackedJavaScriptImportIssues,
packageExportSpecifier,
renderBrowserConsumer,
verifyCliLicense,
} from "./verify-packed-manifests.mjs";
@@ -26,6 +27,15 @@ describe("packed manifest verifier", () => {
);
});
it("keeps every browser package live even when it declares sideEffects false", () => {
const consumer = renderBrowserConsumer(["@hyperframes/parsers", "@hyperframes/lint/browser"]);
assert.match(consumer, /import \* as packedBrowserModule0 from "@hyperframes\/parsers";/);
assert.match(consumer, /import \* as packedBrowserModule1 from "@hyperframes\/lint\/browser";/);
assert.match(consumer, /packedBrowserModules\.map\(\(module\) => Object\.keys\(module\)\)/);
assert.doesNotMatch(consumer, /import "@hyperframes\/parsers"/);
});
it("derives consumer specifiers from the packed export map", () => {
assert.equal(packageExportSpecifier("@hyperframes/sdk", "."), "@hyperframes/sdk");
assert.equal(
@@ -45,8 +55,41 @@ describe("packed manifest verifier", () => {
},
]),
[
{ specifier: "@hyperframes/example", typechecked: true },
{ specifier: "@hyperframes/example/runtime", typechecked: false },
{
specifier: "@hyperframes/example",
typechecked: true,
environments: ["browser", "node"],
},
{
specifier: "@hyperframes/example/runtime",
typechecked: false,
environments: ["browser", "node"],
},
],
);
});
it("uses descriptor environments to separate Node and browser promises", () => {
assert.deepEqual(
listPackedExportContracts([
{
packedPackage: {
name: "@hyperframes/example",
exports: { ".": { import: "./dist/index.js", types: "./dist/index.d.ts" } },
},
descriptor: {
subpaths: {
".": { environments: ["browser"] },
},
},
},
]),
[
{
specifier: "@hyperframes/example",
typechecked: true,
environments: ["browser"],
},
],
);
});