From affa4d53cf86334c18d0476955fce3f4f73f6a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 10 Oct 2026 07:04:34 +0000 Subject: [PATCH] fix(producer): the npm package ships only its own code and no source maps (#5398) * fix(producer): the npm package ships only producer's own code, without source maps * fix(cli): the npm package leaves out Studio's source maps, like its own * fix(producer): the published package finds its workers and pins its own packages exactly --- bun.lock | 10 ++-- packages/cli/scripts/build-copy.mjs | 2 + packages/producer/build.mjs | 60 ++++--------------- packages/producer/buildEntries.mjs | 11 ++++ packages/producer/package.json | 10 ++-- packages/producer/src/buildEntries.test.ts | 15 +++++ .../src/services/shaderTransitionWorker.ts | 6 +- .../services/shaderTransitionWorkerPool.ts | 5 +- 8 files changed, 52 insertions(+), 67 deletions(-) create mode 100644 packages/producer/buildEntries.mjs create mode 100644 packages/producer/src/buildEntries.test.ts diff --git a/bun.lock b/bun.lock index 28dfb581fd..a047d11b09 100644 --- a/bun.lock +++ b/bun.lock @@ -255,11 +255,11 @@ "version": "0.8.128", "dependencies": { "@hono/node-server": "^2.0.5", - "@hyperframes/core": "workspace:^", - "@hyperframes/engine": "workspace:^", - "@hyperframes/lint": "workspace:^", - "@hyperframes/parsers": "workspace:^", - "@hyperframes/studio-server": "workspace:^", + "@hyperframes/core": "workspace:*", + "@hyperframes/engine": "workspace:*", + "@hyperframes/lint": "workspace:*", + "@hyperframes/parsers": "workspace:*", + "@hyperframes/studio-server": "workspace:*", "hono": "^4.6.0", "linkedom": "^0.18.12", "postcss": "^8.4.0", diff --git a/packages/cli/scripts/build-copy.mjs b/packages/cli/scripts/build-copy.mjs index 177ab7d1e7..84026383fa 100644 --- a/packages/cli/scripts/build-copy.mjs +++ b/packages/cli/scripts/build-copy.mjs @@ -37,11 +37,13 @@ function copyDir(src, dest) { cpSync(src, dest, { recursive: true, force: true }); } +// Source maps stay out: the CLI ships none of its own (tsup sourcemap: false). function copyDirContents(src, dest) { for (const entry of readdirSync(src)) { cpSync(join(src, entry), join(dest, entry), { recursive: true, force: true, + filter: (path) => !path.endsWith(".map"), }); } } diff --git a/packages/producer/build.mjs b/packages/producer/build.mjs index 222ccc9ef8..f425a0d172 100644 --- a/packages/producer/build.mjs +++ b/packages/producer/build.mjs @@ -9,69 +9,29 @@ 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"; +import { BUNDLES, WORKERS } from "./buildEntries.mjs"; rmSync("dist", { recursive: true, force: true }); mkdirSync("dist", { recursive: true }); const scriptDir = dirname(fileURLToPath(import.meta.url)); -// The banner provides a real `require` (via createRequire) plus the CJS-only -// `__filename`/`__dirname` globals so esbuild's CJS interop works in ESM output. -// Without `require`, bundled CJS deps (recast, yauzl, etc.) that call -// require("fs") throw "Dynamic require of 'fs' is not supported"; without the -// dirname shims, deps like wawoff2 throw "__dirname is not defined in ES module". -const cjsBanner = { - js: `import { createRequire as __cjsRequire } from 'module'; -import { fileURLToPath as __cjsFileURLToPath } from 'url'; -import { dirname as __cjsDirname } from 'path'; -const require = __cjsRequire(import.meta.url); -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|core)(?:\/.*)?$/ }, (args) => { - const path = workspaceAliases[args.path]; - return path ? { path } : undefined; - }); - }, -}; - const sharedOpts = { bundle: true, platform: "node", target: "node22", format: "esm", - external: ["puppeteer", "esbuild", "postcss"], - plugins: [workspaceAliasPlugin], + // Dependencies install from package.json like any other package's, so the + // tarball carries only producer's own code (as aws-lambda and gcp-cloud-run do). + packages: "external", minify: false, - sourcemap: true, - banner: cjsBanner, }; -await Promise.all([ - build({ ...sharedOpts, entryPoints: ["src/index.ts"], outfile: "dist/index.js" }), - build({ ...sharedOpts, entryPoints: ["src/server.ts"], outfile: "dist/public-server.js" }), - build({ - ...sharedOpts, - entryPoints: ["src/services/shaderTransitionWorker.ts"], - outfile: "dist/services/shaderTransitionWorker.js", - }), - build({ - ...sharedOpts, - entryPoints: ["src/services/healthWorkerThread.ts"], - outfile: "dist/services/healthWorkerThread.js", - }), - build({ ...sharedOpts, entryPoints: ["src/distributed.ts"], outfile: "dist/distributed.js" }), -]); +await Promise.all( + [...BUNDLES, ...WORKERS].map(({ entry, outfile }) => + build({ ...sharedOpts, entryPoints: [entry], outfile }), + ), +); // Copy core runtime artifacts so the producer can find them at dist/ import { copyFileSync, existsSync, readFileSync } from "fs"; @@ -91,7 +51,7 @@ try { // Generate .d.ts declarations (esbuild doesn't emit them) import { execSync } from "child_process"; -execSync("tsc --emitDeclarationOnly --declaration --declarationMap", { +execSync("tsc --emitDeclarationOnly --declaration --declarationMap false", { stdio: "inherit", }); diff --git a/packages/producer/buildEntries.mjs b/packages/producer/buildEntries.mjs new file mode 100644 index 0000000000..b6dc34c708 --- /dev/null +++ b/packages/producer/buildEntries.mjs @@ -0,0 +1,11 @@ +// Workers sit beside the bundles because healthWorker.ts and shaderTransitionWorkerPool.ts +// look for them in their own module's directory. +export const BUNDLES = [ + { entry: "src/index.ts", outfile: "dist/index.js" }, + { entry: "src/server.ts", outfile: "dist/public-server.js" }, + { entry: "src/distributed.ts", outfile: "dist/distributed.js" }, +]; +export const WORKERS = [ + { entry: "src/services/shaderTransitionWorker.ts", outfile: "dist/shaderTransitionWorker.js" }, + { entry: "src/services/healthWorkerThread.ts", outfile: "dist/healthWorkerThread.js" }, +]; diff --git a/packages/producer/package.json b/packages/producer/package.json index 234583cd91..1574c36f07 100644 --- a/packages/producer/package.json +++ b/packages/producer/package.json @@ -76,11 +76,11 @@ }, "dependencies": { "@hono/node-server": "^2.0.5", - "@hyperframes/core": "workspace:^", - "@hyperframes/engine": "workspace:^", - "@hyperframes/lint": "workspace:^", - "@hyperframes/parsers": "workspace:^", - "@hyperframes/studio-server": "workspace:^", + "@hyperframes/core": "workspace:*", + "@hyperframes/engine": "workspace:*", + "@hyperframes/lint": "workspace:*", + "@hyperframes/parsers": "workspace:*", + "@hyperframes/studio-server": "workspace:*", "hono": "^4.6.0", "linkedom": "^0.18.12", "postcss": "^8.4.0", diff --git a/packages/producer/src/buildEntries.test.ts b/packages/producer/src/buildEntries.test.ts new file mode 100644 index 0000000000..8b8bcb2141 --- /dev/null +++ b/packages/producer/src/buildEntries.test.ts @@ -0,0 +1,15 @@ +import { dirname, basename } from "node:path"; +import { describe, expect, it } from "vitest"; +import { BUNDLES, WORKERS } from "../buildEntries.mjs"; + +describe("published build layout", () => { + it("emits each worker beside the bundles whose worker lookup probes their own directory", () => { + const bundleDirs = new Set(BUNDLES.map(({ outfile }) => dirname(outfile))); + expect([...bundleDirs]).toEqual(["dist"]); + expect(WORKERS.map(({ outfile }) => dirname(outfile))).toEqual(["dist", "dist"]); + expect(WORKERS.map(({ outfile }) => basename(outfile)).sort()).toEqual([ + "healthWorkerThread.js", + "shaderTransitionWorker.js", + ]); + }); +}); diff --git a/packages/producer/src/services/shaderTransitionWorker.ts b/packages/producer/src/services/shaderTransitionWorker.ts index 0935d24435..b0ecce70a4 100644 --- a/packages/producer/src/services/shaderTransitionWorker.ts +++ b/packages/producer/src/services/shaderTransitionWorker.ts @@ -53,10 +53,8 @@ import { parentPort } from "node:worker_threads"; // fail to resolve. The subpath sidesteps that by pointing the // resolver straight at the import-free file. // -// 2. In the production esbuild bundle (build.mjs entry -// `src/services/shaderTransitionWorker.ts`) the workspace alias plugin -// redirects `@hyperframes/engine/shader-transitions` to the same TS -// source and bundles it inline, so behavior is identical. +// 2. The published build keeps it external, so it loads from the installed +// engine package, the same import-free file. import { TRANSITIONS, crossfade } from "@hyperframes/engine/shader-transitions"; interface ShaderJobRequest { diff --git a/packages/producer/src/services/shaderTransitionWorkerPool.ts b/packages/producer/src/services/shaderTransitionWorkerPool.ts index e575dc1e3c..70b31d575f 100644 --- a/packages/producer/src/services/shaderTransitionWorkerPool.ts +++ b/packages/producer/src/services/shaderTransitionWorkerPool.ts @@ -137,9 +137,8 @@ export interface ShaderTransitionWorkerPool { * sibling probe lands in the wrong directory. * 2. `HF_SHADER_WORKER_ENTRY` env var — test/dev infra override (file * path or `file://` URL). - * 3. Same-directory `.js` sibling — works when both pool source and - * worker source compile into the same `dist/services/` directory - * (in-tree dev builds and the colocated tsc emit). + * 3. Same-directory `.js` sibling — the published build emits the worker + * beside the bundles that contain this pool (buildEntries.mjs). * 4. Same-directory `.ts` sibling — vitest/bun raw-TS execution path. */ function resolveWorkerEntry(explicit: string | undefined): { path: string; isTs: boolean } {