From f064103e1b1fb888fdec233951f1cc1538ac7dfb Mon Sep 17 00:00:00 2001 From: James Date: Fri, 10 Jul 2026 13:36:29 -0700 Subject: [PATCH] refactor(repo): centralize package subpaths --- package.json | 6 +- packages/aws-lambda/package-subpaths.json | 29 ++ packages/aws-lambda/package.json | 38 ++- packages/cli/tsup.config.ts | 28 +- packages/core/package-subpaths.json | 264 ++++++++++++++++++ packages/core/package.json | 22 +- packages/engine/package-subpaths.json | 29 ++ packages/engine/package.json | 19 +- packages/gcp-cloud-run/package-subpaths.json | 23 ++ packages/gcp-cloud-run/package.json | 29 +- packages/lint/package-subpaths.json | 24 ++ packages/parsers/package-subpaths.json | 90 ++++++ packages/player/package-subpaths.json | 25 ++ packages/producer/build.mjs | 25 +- packages/producer/package-subpaths.json | 23 ++ packages/producer/package.json | 25 +- packages/sdk/package-subpaths.json | 35 +++ packages/sdk/package.json | 5 + .../shader-transitions/package-subpaths.json | 15 + packages/shader-transitions/package.json | 3 +- packages/studio-server/package-subpaths.json | 54 ++++ packages/studio/package-subpaths.json | 23 ++ packages/studio/package.json | 12 +- scripts/package-subpaths.mjs | 225 +++++++++++++++ scripts/package-subpaths.test.mjs | 108 +++++++ 25 files changed, 1103 insertions(+), 76 deletions(-) create mode 100644 packages/aws-lambda/package-subpaths.json create mode 100644 packages/core/package-subpaths.json create mode 100644 packages/engine/package-subpaths.json create mode 100644 packages/gcp-cloud-run/package-subpaths.json create mode 100644 packages/lint/package-subpaths.json create mode 100644 packages/parsers/package-subpaths.json create mode 100644 packages/player/package-subpaths.json create mode 100644 packages/producer/package-subpaths.json create mode 100644 packages/sdk/package-subpaths.json create mode 100644 packages/shader-transitions/package-subpaths.json create mode 100644 packages/studio-server/package-subpaths.json create mode 100644 packages/studio/package-subpaths.json create mode 100644 scripts/package-subpaths.mjs create mode 100644 scripts/package-subpaths.test.mjs diff --git a/package.json b/package.json index 87b144b2e2..b9d79b71b5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "changelog:weekly": "tsx scripts/changelog-weekly.ts", "sync-schemas": "tsx scripts/sync-schemas.ts", "sync-schemas:check": "tsx scripts/sync-schemas.ts --check", - "lint": "bun run check:tracked-artifacts && bun run check:workspace-contracts && bun run check:package-cycles && oxlint . && tsx scripts/lint-skills.ts", + "sync:package-subpaths": "node scripts/package-subpaths.mjs --write", + "check:package-subpaths": "node scripts/package-subpaths.mjs", + "lint": "bun run check:tracked-artifacts && bun run check:workspace-contracts && bun run check:package-cycles && bun run check:package-subpaths && oxlint . && tsx scripts/lint-skills.ts", "lint:skills": "tsx scripts/lint-skills.ts", "lint:fix": "oxlint --fix .", "check:tracked-artifacts": "node scripts/check-tracked-artifacts.mjs", @@ -42,7 +44,7 @@ "player:perf": "bun run --filter @hyperframes/player perf", "format:check": "oxfmt --check .", "knip": "knip", - "test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/validate-release-channel.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs", + "test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs", "test:skills": "node --test 'skills/**/*.test.mjs'", "generate:previews": "tsx scripts/generate-template-previews.ts", "generate:catalog-previews": "tsx scripts/generate-catalog-previews.ts", diff --git a/packages/aws-lambda/package-subpaths.json b/packages/aws-lambda/package-subpaths.json new file mode 100644 index 0000000000..f475528052 --- /dev/null +++ b/packages/aws-lambda/package-subpaths.json @@ -0,0 +1,29 @@ +{ + "package": "@hyperframes/aws-lambda", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["bun", "node"] + }, + "./handler": { + "source": "./src/handler.ts", + "runtime": "./dist/handler.js", + "types": "./dist/handler.d.ts", + "environments": ["bun", "node"] + }, + "./sdk": { + "source": "./src/sdk/index.ts", + "runtime": "./dist/sdk/index.js", + "types": "./dist/sdk/index.d.ts", + "environments": ["bun", "node"] + }, + "./cdk": { + "source": "./src/cdk/index.ts", + "runtime": "./dist/cdk/index.js", + "types": "./dist/cdk/index.d.ts", + "environments": ["bun", "node"] + } + } +} diff --git a/packages/aws-lambda/package.json b/packages/aws-lambda/package.json index cb9b13b13c..81ce216237 100644 --- a/packages/aws-lambda/package.json +++ b/packages/aws-lambda/package.json @@ -17,24 +17,46 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "bun": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" }, "./handler": { - "import": "./dist/handler.js", - "types": "./dist/handler.d.ts" + "bun": "./src/handler.ts", + "import": "./src/handler.ts", + "types": "./src/handler.ts" }, "./sdk": { - "import": "./dist/sdk/index.js", - "types": "./dist/sdk/index.d.ts" + "bun": "./src/sdk/index.ts", + "import": "./src/sdk/index.ts", + "types": "./src/sdk/index.ts" }, "./cdk": { - "import": "./dist/cdk/index.js", - "types": "./dist/cdk/index.d.ts" + "bun": "./src/cdk/index.ts", + "import": "./src/cdk/index.ts", + "types": "./src/cdk/index.ts" } }, "publishConfig": { "access": "public", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./handler": { + "import": "./dist/handler.js", + "types": "./dist/handler.d.ts" + }, + "./sdk": { + "import": "./dist/sdk/index.js", + "types": "./dist/sdk/index.d.ts" + }, + "./cdk": { + "import": "./dist/cdk/index.js", + "types": "./dist/cdk/index.d.ts" + } + }, "registry": "https://registry.npmjs.org/" }, "scripts": { diff --git a/packages/cli/tsup.config.ts b/packages/cli/tsup.config.ts index f3aba1848e..fcc64add5e 100644 --- a/packages/cli/tsup.config.ts +++ b/packages/cli/tsup.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "tsup"; import { resolve } from "node:path"; import { readFileSync } from "node:fs"; +import { sourceAliases } from "../../scripts/package-subpaths.mjs"; const pkg = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf-8")) as { version: string; @@ -79,27 +80,12 @@ var __dirname = __hf_dirname(__filename);`, }, esbuildOptions(options) { options.alias = { - "@hyperframes/producer": resolve(__dirname, "../producer/src/index.ts"), - // esbuild's alias map treats `@hyperframes/producer` as a file path - // and would otherwise resolve `@hyperframes/producer/distributed` - // to `../producer/src/index.ts/distributed` (treating the file as a - // directory). Adding an explicit alias for every subpath we import - // avoids the prefix-substitution misfire. - "@hyperframes/producer/distributed": resolve(__dirname, "../producer/src/distributed.ts"), - // Same reason: the lambda CLI imports `@hyperframes/aws-lambda/sdk`, - // which would resolve to `../aws-lambda/src/index.ts/sdk` without - // an explicit subpath alias. The SDK subpath has its own barrel. - "@hyperframes/aws-lambda/sdk": resolve(__dirname, "../aws-lambda/src/sdk/index.ts"), - // Same for the GCP adapter's SDK subpath barrel. - "@hyperframes/gcp-cloud-run/sdk": resolve(__dirname, "../gcp-cloud-run/src/sdk/index.ts"), - // hf#677 follow-up: the shader-blend worker imports from - // `@hyperframes/engine/shader-transitions` (subpath export) — a - // standalone TS file with zero internal imports that survives the - // worker_thread loader boundary. - "@hyperframes/engine/shader-transitions": resolve( - __dirname, - "../engine/src/utils/shaderTransitions.ts", - ), + // Exact subpaths are generated from the same contracts as package + // exports, avoiding esbuild's root-alias prefix substitution trap. + ...sourceAliases(resolve(__dirname, "../producer"), [".", "./distributed"]), + ...sourceAliases(resolve(__dirname, "../aws-lambda"), ["./sdk"]), + ...sourceAliases(resolve(__dirname, "../gcp-cloud-run"), ["./sdk"]), + ...sourceAliases(resolve(__dirname, "../engine"), [".", "./shader-transitions"]), }; options.loader = { ...options.loader, ".browser.js": "text" }; }, diff --git a/packages/core/package-subpaths.json b/packages/core/package-subpaths.json new file mode 100644 index 0000000000..05a7b9f7d8 --- /dev/null +++ b/packages/core/package-subpaths.json @@ -0,0 +1,264 @@ +{ + "package": "@hyperframes/core", + "localNodeRuntime": true, + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + }, + "./beats": { + "source": "./src/beats/index.ts", + "runtime": "./dist/beats/index.js", + "types": "./dist/beats/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./html-attr-safety": { + "source": "./src/utils/htmlAttrSafety.ts", + "runtime": "./dist/utils/htmlAttrSafety.js", + "types": "./dist/utils/htmlAttrSafety.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./composition-contract": { + "source": "./src/compositionContract.ts", + "runtime": "./dist/compositionContract.js", + "types": "./dist/compositionContract.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./editing": { + "source": "./src/editing/affordances.ts", + "runtime": "./dist/editing/affordances.js", + "types": "./dist/editing/affordances.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./editing/draft-markers": { + "source": "./src/editing/draftMarkers.ts", + "runtime": "./dist/editing/draftMarkers.js", + "types": "./dist/editing/draftMarkers.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./variables": { + "source": "./src/variables.ts", + "runtime": "./dist/variables.js", + "types": "./dist/variables.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./slideshow": { + "source": "./src/slideshow/index.ts", + "runtime": "./dist/slideshow/index.js", + "types": "./dist/slideshow/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./generators": { + "source": "./src/generators/hyperframes.ts", + "runtime": "./dist/generators/hyperframes.js", + "types": "./dist/generators/hyperframes.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./lint": { + "source": "./src/lint/index.ts", + "runtime": "./dist/lint/index.js", + "types": "./dist/lint/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./compiler": { + "source": "./src/compiler/index.ts", + "runtime": "./dist/compiler/index.js", + "types": "./dist/compiler/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./color-grading": { + "source": "./src/colorGrading.ts", + "runtime": "./dist/colorGrading.js", + "types": "./dist/colorGrading.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./color-luts": { + "source": "./src/colorLuts.ts", + "runtime": "./dist/colorLuts.js", + "types": "./dist/colorLuts.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./storyboard": { + "source": "./src/storyboard/index.ts", + "runtime": "./dist/storyboard/index.js", + "types": "./dist/storyboard/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime": { + "source": "./dist/hyperframe.runtime.iife.js", + "runtime": "./dist/hyperframe.runtime.iife.js", + "types": null, + "environments": ["browser"] + }, + "./runtime/clipTree": { + "source": "./src/runtime/clipTree.ts", + "runtime": "./dist/runtime/clipTree.js", + "types": "./dist/runtime/clipTree.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime/start-expression": { + "source": "./src/runtime/startExpression.ts", + "runtime": "./dist/runtime/startExpression.js", + "types": "./dist/runtime/startExpression.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime/protocol": { + "source": "./src/runtime/protocol.ts", + "runtime": "./dist/runtime/protocol.js", + "types": "./dist/runtime/protocol.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./compiler/html-document": { + "source": "./src/compiler/htmlDocument.ts", + "runtime": "./dist/compiler/htmlDocument.js", + "types": "./dist/compiler/htmlDocument.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime/position-edits": { + "source": "./src/runtime/positionEdits.ts", + "runtime": "./dist/runtime/positionEdits.js", + "types": "./dist/runtime/positionEdits.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime/position-edits-render": { + "source": "./src/generated/position-edits-render-inline.ts", + "runtime": "./dist/generated/position-edits-render-inline.js", + "types": "./dist/generated/position-edits-render-inline.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./runtime/lottie-readiness": { + "source": "./src/lottieReadiness.ts", + "runtime": "./dist/lottieReadiness.js", + "types": "./dist/lottieReadiness.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./studio-api": { + "source": "./src/studio-api/index.ts", + "runtime": "./dist/studio-api/index.js", + "types": "./dist/studio-api/index.d.ts", + "environments": ["browser", "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"] + }, + "./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"] + }, + "./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"] + }, + "./studio-api/draft-markers": { + "source": "./src/studio-api/helpers/draftMarkers.ts", + "runtime": "./dist/studio-api/helpers/draftMarkers.js", + "types": "./dist/studio-api/helpers/draftMarkers.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./studio-api/finite-mutation": { + "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"] + }, + "./text": { + "source": "./src/text/index.ts", + "runtime": "./dist/text/index.js", + "types": "./dist/text/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./registry": { + "source": "./src/registry/index.ts", + "runtime": "./dist/registry/index.js", + "types": "./dist/registry/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./media-volume-envelope": { + "source": "./src/runtime/mediaVolumeEnvelope.ts", + "runtime": "./dist/runtime/mediaVolumeEnvelope.js", + "types": "./dist/runtime/mediaVolumeEnvelope.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./hf-ids": { + "source": "./src/parsers/hfIds.ts", + "runtime": "./dist/parsers/hfIds.js", + "types": "./dist/parsers/hfIds.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-parser": { + "source": "./src/parsers/gsapParserExports.ts", + "runtime": "./dist/parsers/gsapParserExports.js", + "types": "./dist/parsers/gsapParserExports.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-parser-acorn": { + "source": "./src/parsers/gsapParserAcorn.ts", + "runtime": "./dist/parsers/gsapParserAcorn.js", + "types": "./dist/parsers/gsapParserAcorn.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-writer-acorn": { + "source": "./src/parsers/gsapWriterAcorn.ts", + "runtime": "./dist/parsers/gsapWriterAcorn.js", + "types": "./dist/parsers/gsapWriterAcorn.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-constants": { + "source": "./src/parsers/gsapConstants.ts", + "runtime": "./dist/parsers/gsapConstants.js", + "types": "./dist/parsers/gsapConstants.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./spring-ease": { + "source": "./src/parsers/springEase.ts", + "runtime": "./dist/parsers/springEase.js", + "types": "./dist/parsers/springEase.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./fonts/aliases": { + "source": "./src/fonts/aliases.ts", + "runtime": "./dist/fonts/aliases.js", + "types": "./dist/fonts/aliases.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./fonts/system-locator": { + "source": "./src/fonts/systemFontLocator.ts", + "runtime": "./dist/fonts/systemFontLocator.js", + "types": "./dist/fonts/systemFontLocator.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./figma": { + "source": "./src/figma/index.ts", + "runtime": "./dist/figma/index.js", + "types": "./dist/figma/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./schemas/registry.json": { + "source": "./schemas/registry.json", + "runtime": "./schemas/registry.json", + "types": null, + "environments": ["browser", "bun", "node"] + }, + "./schemas/registry-item.json": { + "source": "./schemas/registry-item.json", + "runtime": "./schemas/registry-item.json", + "types": null, + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/core/package.json b/packages/core/package.json index a0cbbd8985..3d0c6d7b5c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -288,9 +288,13 @@ "import": "./dist/editing/draftMarkers.js", "types": "./dist/editing/draftMarkers.d.ts" }, - "./figma": { - "import": "./dist/figma/index.js", - "types": "./dist/figma/index.d.ts" + "./variables": { + "import": "./dist/variables.js", + "types": "./dist/variables.d.ts" + }, + "./slideshow": { + "import": "./dist/slideshow/index.js", + "types": "./dist/slideshow/index.d.ts" }, "./generators": { "import": "./dist/generators/hyperframes.js", @@ -300,14 +304,6 @@ "import": "./dist/lint/index.js", "types": "./dist/lint/index.d.ts" }, - "./variables": { - "import": "./dist/variables.js", - "types": "./dist/variables.d.ts" - }, - "./slideshow": { - "import": "./dist/slideshow/index.js", - "types": "./dist/slideshow/index.d.ts" - }, "./compiler": { "import": "./dist/compiler/index.js", "types": "./dist/compiler/index.d.ts" @@ -421,6 +417,10 @@ "import": "./dist/fonts/systemFontLocator.js", "types": "./dist/fonts/systemFontLocator.d.ts" }, + "./figma": { + "import": "./dist/figma/index.js", + "types": "./dist/figma/index.d.ts" + }, "./schemas/registry.json": "./schemas/registry.json", "./schemas/registry-item.json": "./schemas/registry-item.json" }, diff --git a/packages/engine/package-subpaths.json b/packages/engine/package-subpaths.json new file mode 100644 index 0000000000..9d479c690b --- /dev/null +++ b/packages/engine/package-subpaths.json @@ -0,0 +1,29 @@ +{ + "package": "@hyperframes/engine", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./alpha-blit": { + "source": "./src/utils/alphaBlit.ts", + "runtime": "./dist/utils/alphaBlit.js", + "types": "./dist/utils/alphaBlit.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./shader-transitions": { + "source": "./src/utils/shaderTransitions.ts", + "runtime": "./dist/utils/shaderTransitions.js", + "types": "./dist/utils/shaderTransitions.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/engine/package.json b/packages/engine/package.json index 6ee95a695d..a52c6106b5 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -15,9 +15,22 @@ "main": "./src/index.ts", "types": "./src/index.ts", "exports": { - ".": "./src/index.ts", - "./alpha-blit": "./src/utils/alphaBlit.ts", - "./shader-transitions": "./src/utils/shaderTransitions.ts" + ".": { + "bun": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + }, + "./alpha-blit": { + "bun": "./src/utils/alphaBlit.ts", + "import": "./src/utils/alphaBlit.ts", + "types": "./src/utils/alphaBlit.ts" + }, + "./shader-transitions": { + "bun": "./src/utils/shaderTransitions.ts", + "import": "./src/utils/shaderTransitions.ts", + "types": "./src/utils/shaderTransitions.ts" + }, + "./package.json": "./package.json" }, "publishConfig": { "access": "public", diff --git a/packages/gcp-cloud-run/package-subpaths.json b/packages/gcp-cloud-run/package-subpaths.json new file mode 100644 index 0000000000..22cafe9004 --- /dev/null +++ b/packages/gcp-cloud-run/package-subpaths.json @@ -0,0 +1,23 @@ +{ + "package": "@hyperframes/gcp-cloud-run", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["bun", "node"] + }, + "./server": { + "source": "./src/server.ts", + "runtime": "./dist/server.js", + "types": "./dist/server.d.ts", + "environments": ["bun", "node"] + }, + "./sdk": { + "source": "./src/sdk/index.ts", + "runtime": "./dist/sdk/index.js", + "types": "./dist/sdk/index.d.ts", + "environments": ["bun", "node"] + } + } +} diff --git a/packages/gcp-cloud-run/package.json b/packages/gcp-cloud-run/package.json index 1a37324ff0..80b11cc367 100644 --- a/packages/gcp-cloud-run/package.json +++ b/packages/gcp-cloud-run/package.json @@ -18,20 +18,37 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "bun": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" }, "./server": { - "import": "./dist/server.js", - "types": "./dist/server.d.ts" + "bun": "./src/server.ts", + "import": "./src/server.ts", + "types": "./src/server.ts" }, "./sdk": { - "import": "./dist/sdk/index.js", - "types": "./dist/sdk/index.d.ts" + "bun": "./src/sdk/index.ts", + "import": "./src/sdk/index.ts", + "types": "./src/sdk/index.ts" } }, "publishConfig": { "access": "public", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./server": { + "import": "./dist/server.js", + "types": "./dist/server.d.ts" + }, + "./sdk": { + "import": "./dist/sdk/index.js", + "types": "./dist/sdk/index.d.ts" + } + }, "registry": "https://registry.npmjs.org/" }, "scripts": { diff --git a/packages/lint/package-subpaths.json b/packages/lint/package-subpaths.json new file mode 100644 index 0000000000..e7b17976a3 --- /dev/null +++ b/packages/lint/package-subpaths.json @@ -0,0 +1,24 @@ +{ + "package": "@hyperframes/lint", + "localNodeRuntime": true, + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./browser": { + "source": "./src/browser.ts", + "runtime": "./dist/browser.js", + "types": "./dist/browser.d.ts", + "environments": ["browser", "bun"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/parsers/package-subpaths.json b/packages/parsers/package-subpaths.json new file mode 100644 index 0000000000..9fb9fbb7a5 --- /dev/null +++ b/packages/parsers/package-subpaths.json @@ -0,0 +1,90 @@ +{ + "package": "@hyperframes/parsers", + "localNodeRuntime": true, + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + }, + "./gsap-parser": { + "source": "./src/gsapParserExports.ts", + "runtime": "./dist/gsapParserExports.js", + "types": "./dist/gsapParserExports.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-parser-acorn": { + "source": "./src/gsapParserAcorn.ts", + "runtime": "./dist/gsapParserAcorn.js", + "types": "./dist/gsapParserAcorn.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-writer-acorn": { + "source": "./src/gsapWriterAcorn.ts", + "runtime": "./dist/gsapWriterAcorn.js", + "types": "./dist/gsapWriterAcorn.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-constants": { + "source": "./src/gsapConstants.ts", + "runtime": "./dist/gsapConstants.js", + "types": "./dist/gsapConstants.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./spring-ease": { + "source": "./src/springEase.ts", + "runtime": "./dist/springEase.js", + "types": "./dist/springEase.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./hf-ids": { + "source": "./src/hfIds.ts", + "runtime": "./dist/hfIds.js", + "types": "./dist/hfIds.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./gsap-parser-recast": { + "source": "./src/gsapParser.ts", + "runtime": "./dist/gsapParser.js", + "types": "./dist/gsapParser.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./slideshow": { + "source": "./src/slideshow/index.ts", + "runtime": "./dist/slideshow.js", + "types": "./dist/slideshow.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./asset-paths": { + "source": "./src/assets.ts", + "runtime": "./dist/assets.js", + "types": "./dist/assets.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./composition": { + "source": "./src/composition.ts", + "runtime": "./dist/composition.js", + "types": "./dist/composition.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./composition-contract": { + "source": "./src/compositionContract.ts", + "runtime": "./dist/compositionContract.js", + "types": "./dist/compositionContract.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./sub-composition-validity": { + "source": "./src/subCompositionValidity.ts", + "runtime": "./dist/subCompositionValidity.js", + "types": "./dist/subCompositionValidity.d.ts", + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/player/package-subpaths.json b/packages/player/package-subpaths.json new file mode 100644 index 0000000000..e2b0b2fecc --- /dev/null +++ b/packages/player/package-subpaths.json @@ -0,0 +1,25 @@ +{ + "package": "@hyperframes/player", + "subpaths": { + ".": { + "source": "./dist/hyperframes-player.js", + "runtime": { + "script": "./dist/hyperframes-player.global.js", + "import": "./dist/hyperframes-player.js", + "require": "./dist/hyperframes-player.cjs" + }, + "types": "./dist/hyperframes-player.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./slideshow": { + "source": "./dist/slideshow/hyperframes-slideshow.js", + "runtime": { + "script": "./dist/slideshow/hyperframes-slideshow.global.js", + "import": "./dist/slideshow/hyperframes-slideshow.js", + "require": "./dist/slideshow/hyperframes-slideshow.cjs" + }, + "types": "./dist/slideshow/hyperframes-slideshow.d.ts", + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/producer/build.mjs b/packages/producer/build.mjs index b494ef7a3c..222ccc9ef8 100644 --- a/packages/producer/build.mjs +++ b/packages/producer/build.mjs @@ -9,6 +9,7 @@ import { build } from "esbuild"; import { mkdirSync, rmSync } from "fs"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; +import { sourceAliases } from "../../scripts/package-subpaths.mjs"; rmSync("dist", { recursive: true, force: true }); mkdirSync("dist", { recursive: true }); @@ -29,24 +30,18 @@ const __filename = __cjsFileURLToPath(import.meta.url); const __dirname = __cjsDirname(__filename);`, }; +const workspaceAliases = { + ...sourceAliases(resolve(scriptDir, "../engine"), [".", "./alpha-blit", "./shader-transitions"]), + ...sourceAliases(resolve(scriptDir, "../core"), [".", "./lint"]), +}; + const workspaceAliasPlugin = { name: "workspace-alias", setup(build) { - build.onResolve({ filter: /^@hyperframes\/engine$/ }, () => ({ - path: resolve(scriptDir, "../engine/src/index.ts"), - })); - build.onResolve({ filter: /^@hyperframes\/engine\/alpha-blit$/ }, () => ({ - path: resolve(scriptDir, "../engine/src/utils/alphaBlit.ts"), - })); - build.onResolve({ filter: /^@hyperframes\/engine\/shader-transitions$/ }, () => ({ - path: resolve(scriptDir, "../engine/src/utils/shaderTransitions.ts"), - })); - build.onResolve({ filter: /^@hyperframes\/core$/ }, () => ({ - path: resolve(scriptDir, "../core/src/index.ts"), - })); - build.onResolve({ filter: /^@hyperframes\/core\/lint$/ }, () => ({ - path: resolve(scriptDir, "../core/src/lint/index.ts"), - })); + build.onResolve({ filter: /^@hyperframes\/(?:engine|core)(?:\/.*)?$/ }, (args) => { + const path = workspaceAliases[args.path]; + return path ? { path } : undefined; + }); }, }; diff --git a/packages/producer/package-subpaths.json b/packages/producer/package-subpaths.json new file mode 100644 index 0000000000..0bf942ee6c --- /dev/null +++ b/packages/producer/package-subpaths.json @@ -0,0 +1,23 @@ +{ + "package": "@hyperframes/producer", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["bun", "node"] + }, + "./server": { + "source": "./src/server.ts", + "runtime": "./dist/public-server.js", + "types": null, + "environments": ["bun", "node"] + }, + "./distributed": { + "source": "./src/distributed.ts", + "runtime": "./dist/distributed.js", + "types": "./dist/distributed.d.ts", + "environments": ["bun", "node"] + } + } +} diff --git a/packages/producer/package.json b/packages/producer/package.json index 71b1c34612..11131202f4 100644 --- a/packages/producer/package.json +++ b/packages/producer/package.json @@ -15,19 +15,30 @@ "types": "dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - }, - "./server": { - "import": "./dist/public-server.js" + "bun": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" }, + "./server": "./src/server.ts", "./distributed": { - "import": "./dist/distributed.js", - "types": "./dist/distributed.d.ts" + "bun": "./src/distributed.ts", + "import": "./src/distributed.ts", + "types": "./src/distributed.ts" } }, "publishConfig": { "access": "public", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./server": "./dist/public-server.js", + "./distributed": { + "import": "./dist/distributed.js", + "types": "./dist/distributed.d.ts" + } + }, "registry": "https://registry.npmjs.org/" }, "scripts": { diff --git a/packages/sdk/package-subpaths.json b/packages/sdk/package-subpaths.json new file mode 100644 index 0000000000..c8b408775f --- /dev/null +++ b/packages/sdk/package-subpaths.json @@ -0,0 +1,35 @@ +{ + "package": "@hyperframes/sdk", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./adapters/memory": { + "source": "./src/adapters/memory.ts", + "runtime": "./dist/adapters/memory.js", + "types": "./dist/adapters/memory.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./adapters/fs": { + "source": "./src/adapters/fs.ts", + "runtime": "./dist/adapters/fs.js", + "types": "./dist/adapters/fs.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./adapters/headless": { + "source": "./src/adapters/headless.ts", + "runtime": "./dist/adapters/headless.js", + "types": "./dist/adapters/headless.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./editing": { + "source": "./src/editing/affordances.ts", + "runtime": "./dist/editing/affordances.js", + "types": "./dist/editing/affordances.d.ts", + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4e7f06ecb7..336010c87d 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -15,22 +15,27 @@ "sideEffects": false, "exports": { ".": { + "bun": "./src/index.ts", "import": "./src/index.ts", "types": "./src/index.ts" }, "./adapters/memory": { + "bun": "./src/adapters/memory.ts", "import": "./src/adapters/memory.ts", "types": "./src/adapters/memory.ts" }, "./adapters/fs": { + "bun": "./src/adapters/fs.ts", "import": "./src/adapters/fs.ts", "types": "./src/adapters/fs.ts" }, "./adapters/headless": { + "bun": "./src/adapters/headless.ts", "import": "./src/adapters/headless.ts", "types": "./src/adapters/headless.ts" }, "./editing": { + "bun": "./src/editing/affordances.ts", "import": "./src/editing/affordances.ts", "types": "./src/editing/affordances.ts" } diff --git a/packages/shader-transitions/package-subpaths.json b/packages/shader-transitions/package-subpaths.json new file mode 100644 index 0000000000..8ae0c6684a --- /dev/null +++ b/packages/shader-transitions/package-subpaths.json @@ -0,0 +1,15 @@ +{ + "package": "@hyperframes/shader-transitions", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": { + "script": "./dist/index.global.js", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/shader-transitions/package.json b/packages/shader-transitions/package.json index 8a7e62dd28..027a9ee8ef 100644 --- a/packages/shader-transitions/package.json +++ b/packages/shader-transitions/package.json @@ -15,9 +15,10 @@ "types": "./src/index.ts", "exports": { ".": { + "bun": "./src/index.ts", + "import": "./src/index.ts", "types": "./src/index.ts", "script": "./dist/index.global.js", - "import": "./src/index.ts", "require": "./dist/index.cjs" } }, diff --git a/packages/studio-server/package-subpaths.json b/packages/studio-server/package-subpaths.json new file mode 100644 index 0000000000..ffabff8787 --- /dev/null +++ b/packages/studio-server/package-subpaths.json @@ -0,0 +1,54 @@ +{ + "package": "@hyperframes/studio-server", + "localNodeRuntime": true, + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + }, + "./screenshot-clip": { + "source": "./src/helpers/screenshotClip.ts", + "runtime": "./dist/helpers/screenshotClip.js", + "types": "./dist/helpers/screenshotClip.d.ts", + "environments": ["browser", "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"] + }, + "./studio-motion-render-script": { + "source": "./src/helpers/studioMotionRenderScript.ts", + "runtime": "./dist/helpers/studioMotionRenderScript.js", + "types": "./dist/helpers/studioMotionRenderScript.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./draft-markers": { + "source": "./src/helpers/draftMarkers.ts", + "runtime": "./dist/helpers/draftMarkers.js", + "types": "./dist/helpers/draftMarkers.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./finite-mutation": { + "source": "./src/helpers/finiteMutation.ts", + "runtime": "./dist/helpers/finiteMutation.js", + "types": "./dist/helpers/finiteMutation.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./source-mutation": { + "source": "./src/helpers/sourceMutation.ts", + "runtime": "./dist/helpers/sourceMutation.js", + "types": "./dist/helpers/sourceMutation.d.ts", + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/studio/package-subpaths.json b/packages/studio/package-subpaths.json new file mode 100644 index 0000000000..a7a5c7b6fb --- /dev/null +++ b/packages/studio/package-subpaths.json @@ -0,0 +1,23 @@ +{ + "package": "@hyperframes/studio", + "subpaths": { + ".": { + "source": "./src/index.ts", + "runtime": "./dist/index.js", + "types": "./dist/index.d.ts", + "environments": ["browser", "bun", "node"] + }, + "./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"] + }, + "./package.json": { + "source": "./package.json", + "runtime": "./package.json", + "types": null, + "environments": ["browser", "bun", "node"] + } + } +} diff --git a/packages/studio/package.json b/packages/studio/package.json index e99e245a40..1aa534860c 100644 --- a/packages/studio/package.json +++ b/packages/studio/package.json @@ -15,8 +15,16 @@ "main": "./src/index.ts", "types": "./src/index.ts", "exports": { - ".": "./src/index.ts", - "./tailwind-preset": "./src/styles/tailwind-preset.ts", + ".": { + "bun": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts" + }, + "./tailwind-preset": { + "bun": "./src/styles/tailwind-preset.ts", + "import": "./src/styles/tailwind-preset.ts", + "types": "./src/styles/tailwind-preset.ts" + }, "./package.json": "./package.json" }, "publishConfig": { diff --git a/scripts/package-subpaths.mjs b/scripts/package-subpaths.mjs new file mode 100644 index 0000000000..9e10e902df --- /dev/null +++ b/scripts/package-subpaths.mjs @@ -0,0 +1,225 @@ +// Manifest condition normalization is deliberately branch-heavy; every shape +// is covered by package-subpaths.test.mjs and the clean packed-consumer gate. +// fallow-ignore-file complexity +import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = join(import.meta.dirname, ".."); +const DESCRIPTOR_FILE = "package-subpaths.json"; + +function readJson(path) { + return JSON.parse(readFileSync(path, "utf8")); +} + +function sourceTarget(target) { + if (typeof target === "string") return target; + return target?.bun ?? target?.browser ?? target?.import ?? target?.node; +} + +function runtimeTarget(target) { + if (typeof target === "string") return target; + if (!target) return undefined; + const conditions = Object.fromEntries( + Object.entries(target).filter( + ([condition]) => !["types", "bun", "browser"].includes(condition), + ), + ); + return Object.keys(conditions).length === 1 && conditions.import ? conditions.import : conditions; +} + +function typeTarget(target) { + return typeof target === "object" && target !== null ? target.types : undefined; +} + +function inferEnvironments(localTarget, types) { + if (typeof localTarget === "object" && localTarget?.browser) return ["browser", "bun"]; + if (!types && String(sourceTarget(localTarget)).includes("runtime.iife")) return ["browser"]; + return ["browser", "bun", "node"]; +} + +function primaryRuntime(runtime) { + return typeof runtime === "string" + ? runtime + : (runtime.node ?? runtime.import ?? runtime.require ?? runtime.script); +} + +function runtimeConditions(runtime) { + return typeof runtime === "string" ? { import: runtime } : runtime; +} + +export function descriptorFromManifest(pkg) { + const localExports = pkg.exports ?? {}; + const publishedExports = pkg.publishConfig?.exports ?? {}; + const localNodeRuntime = Object.values(localExports).some( + (target) => typeof target === "object" && target !== null && target.node != null, + ); + const keys = [...Object.keys(localExports)]; + for (const key of Object.keys(publishedExports)) { + if (!keys.includes(key)) keys.push(key); + } + + return { + package: pkg.name, + ...(localNodeRuntime ? { localNodeRuntime: true } : {}), + subpaths: Object.fromEntries( + keys.map((key) => { + const localTarget = localExports[key]; + const publishedTarget = publishedExports[key]; + const source = sourceTarget(localTarget ?? publishedTarget); + const runtime = runtimeTarget(publishedTarget ?? localTarget); + const types = typeTarget(publishedTarget ?? localTarget); + return [ + key, + { + source, + runtime, + types: types ?? null, + environments: inferEnvironments(localTarget, types), + }, + ]; + }), + ), + }; +} + +function localExport(contract, { localNodeRuntime = false } = {}) { + const runtime = primaryRuntime(contract.runtime); + const dualManifest = contract.source !== runtime; + if (!dualManifest) return publishedExport(contract); + if (!contract.types) return contract.source; + const browserOnly = + contract.environments.includes("browser") && !contract.environments.includes("node"); + if (browserOnly) { + return { + browser: contract.source, + bun: contract.source, + import: contract.source, + types: contract.source, + }; + } + const conditions = runtimeConditions(contract.runtime); + return { + bun: contract.source, + ...(localNodeRuntime ? { node: conditions.node ?? conditions.import } : {}), + import: contract.source, + types: contract.source, + ...Object.fromEntries( + Object.entries(conditions).filter(([condition]) => !["node", "import"].includes(condition)), + ), + }; +} + +function publishedExport(contract) { + if (typeof contract.runtime === "string") { + if (!contract.types) return contract.runtime; + return { import: contract.runtime, types: contract.types }; + } + return { + ...(contract.types ? { types: contract.types } : {}), + ...runtimeConditions(contract.runtime), + }; +} + +export function exportsFromDescriptor(descriptor) { + return { + local: Object.fromEntries( + Object.entries(descriptor.subpaths).map(([key, contract]) => [ + key, + localExport(contract, { localNodeRuntime: descriptor.localNodeRuntime === true }), + ]), + ), + published: Object.fromEntries( + Object.entries(descriptor.subpaths).map(([key, contract]) => [ + key, + publishedExport(contract), + ]), + ), + }; +} + +export function sourceAliasEntries(descriptor, packageDir, exportKeys) { + const selected = exportKeys ? new Set(exportKeys) : null; + return Object.entries(descriptor.subpaths).flatMap(([key, contract]) => { + if (selected && !selected.has(key)) return []; + if (!contract.source?.match(/\.[cm]?[jt]sx?$/)) return []; + const specifier = key === "." ? descriptor.package : `${descriptor.package}/${key.slice(2)}`; + return [[specifier, resolve(packageDir, contract.source)]]; + }); +} + +export function readPackageSubpaths(packageDir) { + return readJson(join(packageDir, DESCRIPTOR_FILE)); +} + +export function sourceAliases(packageDir, exportKeys) { + return Object.fromEntries( + sourceAliasEntries(readPackageSubpaths(packageDir), packageDir, exportKeys), + ); +} + +export function listPublicWorkspacePackageDirs(root = ROOT) { + const packagesDir = join(root, "packages"); + return readdirSync(packagesDir) + .sort() + .map((name) => join(packagesDir, name)) + .filter((packageDir) => { + const manifestPath = join(packageDir, "package.json"); + if (!existsSync(manifestPath)) return false; + const pkg = readJson(manifestPath); + return pkg.private !== true && pkg.exports != null; + }); +} + +function stableJson(value) { + return `${JSON.stringify(value, null, 2)}\n`; +} + +export function syncPackageManifest(packageDir, { write = false } = {}) { + const manifestPath = join(packageDir, "package.json"); + const descriptorPath = join(packageDir, DESCRIPTOR_FILE); + const pkg = readJson(manifestPath); + const descriptor = readJson(descriptorPath); + if (descriptor.package !== pkg.name) { + throw new Error(`${descriptorPath}: package must equal ${pkg.name}`); + } + const generated = exportsFromDescriptor(descriptor); + const next = structuredClone(pkg); + next.exports = generated.local; + const needsPublishedExports = Object.values(descriptor.subpaths).some( + (contract) => contract.source !== primaryRuntime(contract.runtime), + ); + if (needsPublishedExports) { + next.publishConfig ??= {}; + next.publishConfig.exports = generated.published; + } else if (next.publishConfig?.exports) { + delete next.publishConfig.exports; + } + const expected = stableJson(next); + const actual = readFileSync(manifestPath, "utf8"); + if (actual === expected) return false; + if (!write) throw new Error(`${manifestPath} is out of sync; run bun run sync:package-subpaths`); + writeFileSync(manifestPath, expected); + return true; +} + +function bootstrap(packageDir) { + const manifestPath = join(packageDir, "package.json"); + const pkg = readJson(manifestPath); + writeFileSync(join(packageDir, DESCRIPTOR_FILE), stableJson(descriptorFromManifest(pkg))); +} + +function main() { + const args = new Set(process.argv.slice(2)); + const packageDirs = listPublicWorkspacePackageDirs(); + if (args.has("--bootstrap")) packageDirs.forEach(bootstrap); + const changed = packageDirs.filter((packageDir) => + syncPackageManifest(packageDir, { write: args.has("--write") || args.has("--bootstrap") }), + ); + const action = args.has("--write") || args.has("--bootstrap") ? "synchronized" : "verified"; + console.log( + `Package subpaths ${action}: ${packageDirs.length} public workspaces${changed.length ? ` (${changed.length} updated)` : ""}.`, + ); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) main(); diff --git a/scripts/package-subpaths.test.mjs b/scripts/package-subpaths.test.mjs new file mode 100644 index 0000000000..08d0676f7a --- /dev/null +++ b/scripts/package-subpaths.test.mjs @@ -0,0 +1,108 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; +import { + descriptorFromManifest, + exportsFromDescriptor, + sourceAliasEntries, +} from "./package-subpaths.mjs"; + +describe("package subpath contracts", () => { + const descriptor = { + package: "@hyperframes/example", + subpaths: { + ".": { + source: "./src/index.ts", + runtime: "./dist/index.js", + types: "./dist/index.d.ts", + environments: ["browser", "bun", "node"], + }, + "./browser": { + source: "./src/browser.ts", + runtime: "./dist/browser.js", + types: "./dist/browser.d.ts", + environments: ["browser", "bun"], + }, + "./package.json": { + source: "./package.json", + runtime: "./package.json", + types: null, + environments: ["browser", "bun", "node"], + }, + "./prebuilt": { + source: "./dist/prebuilt.js", + runtime: { + import: "./dist/prebuilt.js", + require: "./dist/prebuilt.cjs", + script: "./dist/prebuilt.global.js", + }, + types: "./dist/prebuilt.d.ts", + environments: ["browser", "bun", "node"], + }, + }, + }; + + it("generates local and published exports from one descriptor", () => { + assert.deepEqual(exportsFromDescriptor(descriptor), { + local: { + ".": { + bun: "./src/index.ts", + import: "./src/index.ts", + types: "./src/index.ts", + }, + "./browser": { + browser: "./src/browser.ts", + bun: "./src/browser.ts", + import: "./src/browser.ts", + types: "./src/browser.ts", + }, + "./package.json": "./package.json", + "./prebuilt": { + types: "./dist/prebuilt.d.ts", + import: "./dist/prebuilt.js", + require: "./dist/prebuilt.cjs", + script: "./dist/prebuilt.global.js", + }, + }, + published: { + ".": { import: "./dist/index.js", types: "./dist/index.d.ts" }, + "./browser": { import: "./dist/browser.js", types: "./dist/browser.d.ts" }, + "./package.json": "./package.json", + "./prebuilt": { + types: "./dist/prebuilt.d.ts", + import: "./dist/prebuilt.js", + require: "./dist/prebuilt.cjs", + script: "./dist/prebuilt.global.js", + }, + }, + }); + }); + + it("derives exact build aliases from selected public subpaths", () => { + assert.deepEqual(sourceAliasEntries(descriptor, "/repo/packages/example", ["./browser"]), [ + ["@hyperframes/example/browser", "/repo/packages/example/src/browser.ts"], + ]); + }); + + it("preserves packages that intentionally resolve local Node imports from dist", () => { + const nodeRuntimeDescriptor = { ...descriptor, localNodeRuntime: true }; + const generated = exportsFromDescriptor(nodeRuntimeDescriptor); + assert.equal(generated.local["."].node, "./dist/index.js"); + assert.equal( + descriptorFromManifest({ + name: nodeRuntimeDescriptor.package, + exports: generated.local, + publishConfig: { exports: generated.published }, + }).localNodeRuntime, + true, + ); + }); + + it("round-trips an existing dual manifest into a descriptor", () => { + const manifest = { + name: "@hyperframes/example", + exports: exportsFromDescriptor(descriptor).local, + publishConfig: { exports: exportsFromDescriptor(descriptor).published }, + }; + assert.deepEqual(descriptorFromManifest(manifest), descriptor); + }); +});