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
This commit is contained in:
Miguel Ángel
2026-10-10 07:04:34 +00:00
committed by GitHub
parent 6f3f86a9c9
commit affa4d53cf
8 changed files with 52 additions and 67 deletions
+5 -5
View File
@@ -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",
+2
View File
@@ -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"),
});
}
}
+10 -50
View File
@@ -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",
});
+11
View File
@@ -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" },
];
+5 -5
View File
@@ -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",
@@ -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",
]);
});
});
@@ -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 {
@@ -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 } {