fix: use pnpm publish for workspace packages

This commit is contained in:
Miguel Ángel
2026-03-26 15:32:04 -04:00
parent 5980af43ca
commit d53596d92c
8 changed files with 27 additions and 146 deletions
+7 -5
View File
@@ -21,37 +21,39 @@ jobs:
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
- run: corepack enable
- run: corepack prepare pnpm@10.17.1 --activate
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run verify:packed-manifests
- name: Publish @hyperframes/core
continue-on-error: true
run: npm --workspace packages/core publish --access public --no-git-checks
run: pnpm --filter @hyperframes/core publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @hyperframes/engine
continue-on-error: true
run: npm --workspace packages/engine publish --access public --no-git-checks
run: pnpm --filter @hyperframes/engine publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @hyperframes/producer
continue-on-error: true
run: npm --workspace packages/producer publish --access public --no-git-checks
run: pnpm --filter @hyperframes/producer publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @hyperframes/studio
continue-on-error: true
run: npm --workspace packages/studio publish --access public --no-git-checks
run: pnpm --filter @hyperframes/studio publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish hyperframes (CLI)
continue-on-error: true
run: npm --workspace packages/cli publish --access public --no-git-checks
run: pnpm --filter @hyperframes/cli publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+1 -3
View File
@@ -15,9 +15,7 @@
"build:studio": "cd ../studio && bun run build",
"build:runtime": "tsx scripts/build-runtime.ts",
"build:copy": "mkdir -p dist/studio dist/docs dist/templates && cp -r ../studio/dist/* dist/studio/ && cp -r src/templates/blank src/templates/warm-grain src/templates/play-mode src/templates/swiss-grid src/templates/vignelli dist/templates/ && (cp src/docs/*.md dist/docs/ 2>/dev/null || true)",
"typecheck": "tsc --noEmit",
"prepack": "node ../../scripts/prepare-package-manifest-for-publish.mjs",
"postpack": "node ../../scripts/restore-package-manifest-after-pack.mjs"
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^1.8.0",
+1 -3
View File
@@ -11,9 +11,7 @@
"scripts": {
"build": "tsc",
"test": "vitest run",
"typecheck": "tsc --noEmit",
"prepack": "node ../../scripts/prepare-package-manifest-for-publish.mjs",
"postpack": "node ../../scripts/restore-package-manifest-after-pack.mjs"
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^1.13.0",
-2
View File
@@ -36,8 +36,6 @@
"docker:build:test": "docker build -f ../../Dockerfile.test -t hyperframes-producer:test ../..",
"docker:test": "docker run --rm --security-opt seccomp=unconfined --shm-size=2g -v ./tests:/app/packages/producer/tests hyperframes-producer:test",
"docker:test:update": "docker run --rm --security-opt seccomp=unconfined --shm-size=2g -v ./tests:/app/packages/producer/tests hyperframes-producer:test --update",
"prepack": "node ../../scripts/prepare-package-manifest-for-publish.mjs",
"postpack": "node ../../scripts/restore-package-manifest-after-pack.mjs",
"prepublishOnly": "echo skip"
},
"dependencies": {
+1 -3
View File
@@ -15,9 +15,7 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit",
"prepack": "node ../../scripts/prepare-package-manifest-for-publish.mjs",
"postpack": "node ../../scripts/restore-package-manifest-after-pack.mjs"
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@codemirror/autocomplete": "^6.20.1",
@@ -1,87 +0,0 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
const ROOT = join(import.meta.dirname, "..");
const PACKAGES_DIR = join(ROOT, "packages");
const BACKUP_DIR = join(tmpdir(), "hyperframes-publish-manifest-backups");
const DEP_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
function loadPackageJson(path) {
return JSON.parse(readFileSync(path, "utf8"));
}
function listWorkspacePackages() {
return readdirSync(PACKAGES_DIR)
.map((dir) => join(PACKAGES_DIR, dir, "package.json"))
.filter((path) => existsSync(path));
}
function getBackupPath(packageJsonPath) {
const digest = createHash("sha256").update(packageJsonPath).digest("hex");
return join(BACKUP_DIR, `${digest}.json`);
}
function resolveWorkspaceSpec(spec, version, packageJsonPath, depName) {
const workspaceSpec = spec.slice("workspace:".length);
if (workspaceSpec === "" || workspaceSpec === "*") return version;
if (workspaceSpec === "^") return `^${version}`;
if (workspaceSpec === "~") return `~${version}`;
throw new Error(
`Unsupported workspace spec "${spec}" for ${depName} in ${packageJsonPath}. ` +
"Update the publish rewrite script before publishing this package.",
);
}
function main() {
const packageJsonPath = join(process.cwd(), "package.json");
const pkg = loadPackageJson(packageJsonPath);
const workspacePackages = new Map(
listWorkspacePackages().map((path) => {
const workspacePkg = loadPackageJson(path);
return [workspacePkg.name, workspacePkg.version];
}),
);
mkdirSync(BACKUP_DIR, { recursive: true });
const backupPath = getBackupPath(packageJsonPath);
try {
readFileSync(backupPath, "utf8");
} catch {
writeFileSync(backupPath, `${JSON.stringify(pkg, null, 2)}\n`);
}
let rewrites = 0;
for (const field of DEP_FIELDS) {
const deps = pkg[field];
if (!deps) continue;
for (const [depName, spec] of Object.entries(deps)) {
if (!String(spec).startsWith("workspace:")) continue;
const depVersion = workspacePackages.get(depName);
if (!depVersion) {
throw new Error(`Cannot resolve workspace dependency ${depName} in ${packageJsonPath}`);
}
const resolvedSpec = resolveWorkspaceSpec(spec, depVersion, packageJsonPath, depName);
deps[depName] = resolvedSpec;
rewrites += 1;
console.log(`${pkg.name} ${field} ${depName}: ${spec} -> ${resolvedSpec}`);
}
}
if (rewrites > 0) {
writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
}
console.log(`Prepared ${pkg.name} for packing by rewriting ${rewrites} workspace reference(s).`);
}
main();
@@ -1,32 +0,0 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import { readFileSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
const BACKUP_DIR = join(tmpdir(), "hyperframes-publish-manifest-backups");
function getBackupPath(packageJsonPath) {
const digest = createHash("sha256").update(packageJsonPath).digest("hex");
return join(BACKUP_DIR, `${digest}.json`);
}
function main() {
const packageJsonPath = join(process.cwd(), "package.json");
const backupPath = getBackupPath(packageJsonPath);
let backup;
try {
backup = readFileSync(backupPath, "utf8");
} catch {
console.log(`No publish-manifest backup found for ${packageJsonPath}; nothing to restore.`);
return;
}
writeFileSync(packageJsonPath, backup);
rmSync(backupPath, { force: true });
console.log(`Restored ${packageJsonPath} after packing.`);
}
main();
+17 -11
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
const ROOT = join(import.meta.dirname, "..");
const PACKAGES_DIR = join(ROOT, "packages");
@@ -29,12 +30,12 @@ function listWorkspaceRefs(pkg) {
}
function parsePackJson(output, workspace) {
const match = output.match(/(\[\s*{[\s\S]*}\s*])\s*$/);
if (!match) {
throw new Error(`Could not parse npm pack JSON output for ${workspace}`);
try {
const parsed = JSON.parse(output);
return Array.isArray(parsed) ? parsed : [parsed];
} catch {
throw new Error(`Could not parse pnpm pack JSON output for ${workspace}`);
}
return JSON.parse(match[1]);
}
function main() {
@@ -44,10 +45,15 @@ function main() {
);
if (listWorkspaceRefs(sourcePackageJson).length === 0) continue;
const packOutput = execFileSync("npm", ["pack", "--json", "--workspace", workspace], {
cwd: ROOT,
encoding: "utf8",
});
const packDir = mkdtempSync(join(tmpdir(), "hyperframes-pack-"));
const packOutput = execFileSync(
"pnpm",
["pack", "--json", "--pack-destination", packDir],
{
cwd: join(ROOT, workspace),
encoding: "utf8",
},
);
const [{ filename }] = parsePackJson(packOutput, workspace);
try {
@@ -65,7 +71,7 @@ function main() {
console.log(`Verified ${workspace}: packed manifest is publish-safe.`);
} finally {
rmSync(join(ROOT, filename), { force: true });
rmSync(packDir, { force: true, recursive: true });
}
}
}