mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Source rename across the monorepo. Every publishable package now ships
under its @hexclave/* name natively, no rewrite-at-publish indirection.
Workflow + tooling:
- Delete scripts/rewrite-packages-to-hexclave.ts (one-shot mirror).
- Remove the mirror-publish block from .github/workflows/npm-publish.yaml.
The remaining `pnpm publish -r` step publishes @hexclave/* natively.
- Flip the auto-bump changeset target from @stackframe/stack to
@hexclave/next so 'Update package versions on dev' keeps working.
- Delete packages/template/src/internal/deprecation-warning.ts and its
imports — @hexclave/* never warns about itself, and after PR 3 no
@stackframe/* artifact is ever built from source again.
Package renames (publishable):
@stackframe/react → @hexclave/react
@stackframe/stack → @hexclave/next
@stackframe/js → @hexclave/js
@stackframe/stack-shared → @hexclave/shared
@stackframe/stack-ui → @hexclave/ui
@stackframe/stack-sc → @hexclave/sc
@stackframe/stack-cli → @hexclave/cli
@stackframe/tanstack-start → @hexclave/tanstack-start
@stackframe/dashboard-ui-components → @hexclave/dashboard-ui-components
Internal monorepo packages (private, never published) also renamed for
brand consistency: backend, dashboard, docs, mcp, skills, e2e-tests,
example apps, the swift-sdk, the monorepo root, etc. Cost is mechanical;
payoff is no stray @stackframe/* names left under apps/, examples/, sdks/.
Carve-outs intentionally kept under their legacy names:
- @stackframe/emails — virtual module imported by customer-stored email
templates; the renderer in apps/backend/src/lib/email-rendering.tsx
dual-aliases both names to the same backing module indefinitely.
- @stackframe/template — internal codegen source, never published; per
docs-mintlify/migration.mdx 'internal packages keep names'.
- @stackframe/init-stack — deprecated; now marked private: true so the
last published version on npm continues to serve old install commands
but the workspace stops publishing it.
Backward-compat detection (so projects still on the last @stackframe/*
release keep working):
- packages/stack-shared/src/config-rendering.ts — CONFIG_IMPORT_PACKAGES
table includes both @hexclave/* (canonical, first match wins) and
legacy @stackframe/* names. Function renamed
detectStackframeImportPackage → detectConfigImportPackage.
- apps/dashboard/src/lib/github-config-push.ts — import detection regex
now matches both @hexclave/<name> and @stackframe/<name>, hexclave
preferred.
Versions: every renamed package reset to 1.0.0 in source. The repo's
existing 'bump versions before merging to main' flow will move them to
1.0.1 on the first publish run, so the dual-publish 1.0.0 from PR 2 is
not overwritten.
Other touch-ups discovered during sweep:
- Root package.json: 'fern' script filter was @stackframe/docs (legacy
typo, never resolved) → @hexclave/docs.
- README.md contributor note: @stackframe/XYZ → @hexclave/XYZ.
- packages/stack-cli/package.json: register `hexclave` bin alongside
the legacy `stack` bin so `npx @hexclave/cli init` works on the
natively-published artifact (PR 1481's rewrite script did this at
publish time; now it's in source).
- packages/template/package-template.json: per-platform names + version
flipped to hexclave + 1.0.0 to stay in sync with generated package.json.
- docs/package.json (legacy fumadocs folder, otherwise carved out of the
brand sweep): workspace deps and name updated minimally so `pnpm
install` resolves — content (MDX) intentionally untouched per the
PR 2 scoping decision.
Carve-out files (skipped entirely by the sweep, intentional history):
- docs-mintlify/migration.mdx — teaches the rename, references both.
- RENAME-TO-HEXCLAVE.md — planning doc, references both indefinitely.
- legacy docs/ folder — content untouched per PR 2 carve-out.
generate-sdks regenerated packages/{react,stack,js} from template.
pnpm-lock.yaml regenerated. Typecheck green on stack-shared, stack, js,
react. Dashboard typecheck has pre-existing 'X is of type unknown'
errors that need to be investigated separately (likely a local
node_modules build state issue, not source).
132 lines
4.5 KiB
TypeScript
132 lines
4.5 KiB
TypeScript
import { spawn } from "child_process";
|
|
import path from "path";
|
|
import { setTimeout as sleep } from "timers/promises";
|
|
|
|
// Root `pnpm run dev` starts eager generators and package watch builds in
|
|
// parallel. `generate-openapi-docs:watch` intentionally runs `codegen-docs`
|
|
// once before starting chokidar, because chokidar only responds to future file
|
|
// changes. Without that initial run, dev docs could serve stale OpenAPI JSON
|
|
// from a previous branch, or no generated JSON at all after a clean checkout,
|
|
// until someone edits an API route.
|
|
//
|
|
// That eager OpenAPI generation imports backend modules, and some of those
|
|
// backend modules resolve workspace packages through their built `dist`
|
|
// entrypoints. Package watch scripts update those entrypoints with
|
|
// `tsdown --watch`, but on a cold checkout, after `pnpm clean`, or during the
|
|
// first package watcher build, the entrypoints may not exist yet even though
|
|
// `tsdown --watch` is about to create them.
|
|
//
|
|
// We keep this wait scoped to the eager generator rather than putting it in
|
|
// front of backend `dev`: the long-running Next dev server can tolerate package
|
|
// watchers warming up, while a one-shot generator exits immediately on a missing
|
|
// import and `concurrently -k` then tears down the whole dev command. Package
|
|
// watch scripts also avoid deleting `dist` in dev mode, which removes the
|
|
// common restart race; this probe covers the remaining cold-start case.
|
|
//
|
|
// This probe waits only for the package imports that the backend-side generator
|
|
// needs. It does not hide real runtime errors: we retry missing-module failures
|
|
// while package builds warm up, and fail immediately for other import failures.
|
|
//
|
|
// In addition to workspace packages, the probe checks that the generated Prisma
|
|
// client exists. When `turbo run dev` starts the backend, `codegen-prisma:watch`
|
|
// (`prisma generate --watch`) performs an initial generation that briefly removes
|
|
// and recreates `src/generated/prisma/`. If `codegen-docs` runs during that
|
|
// window it fails with ERR_MODULE_NOT_FOUND for `@/generated/prisma/client`.
|
|
const repoRoot = path.resolve(__dirname, "..");
|
|
const backendDir = path.join(repoRoot, "apps/backend");
|
|
const timeoutMs = 60_000;
|
|
const retryDelayMs = 1_000;
|
|
|
|
const probeScript = `
|
|
(async () => {
|
|
await import('@hexclave/next');
|
|
await import('@hexclave/shared/dist/utils/env');
|
|
const { existsSync, readdirSync } = await import('node:fs');
|
|
const { join } = await import('node:path');
|
|
const generatedDir = join(process.cwd(), 'src', 'generated', 'prisma');
|
|
if (!existsSync(generatedDir) || readdirSync(generatedDir).length === 0) {
|
|
const err = new Error('ERR_MODULE_NOT_FOUND: Generated Prisma client not yet available at ' + generatedDir);
|
|
throw err;
|
|
}
|
|
})().then(
|
|
() => undefined,
|
|
(error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
},
|
|
);
|
|
`;
|
|
|
|
type ProbeResult = {
|
|
exitCode: number | null,
|
|
output: string,
|
|
};
|
|
|
|
function runProbe(): Promise<ProbeResult> {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn("pnpm", ["exec", "tsx", "-e", probeScript], {
|
|
cwd: backendDir,
|
|
env: process.env,
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
});
|
|
|
|
let output = "";
|
|
|
|
child.stdout.setEncoding("utf8");
|
|
child.stderr.setEncoding("utf8");
|
|
child.stdout.on("data", (chunk: string) => {
|
|
output += chunk;
|
|
});
|
|
child.stderr.on("data", (chunk: string) => {
|
|
output += chunk;
|
|
});
|
|
|
|
child.on("error", reject);
|
|
child.on("close", (exitCode) => {
|
|
resolve({ exitCode, output });
|
|
});
|
|
});
|
|
}
|
|
|
|
function isMissingModuleError(output: string) {
|
|
return output.includes("ERR_MODULE_NOT_FOUND") || output.includes("MODULE_NOT_FOUND");
|
|
}
|
|
|
|
async function main() {
|
|
const start = performance.now();
|
|
let lastOutput = "";
|
|
let hasLoggedWait = false;
|
|
let isReady = false;
|
|
|
|
while (performance.now() - start < timeoutMs) {
|
|
const result = await runProbe();
|
|
if (result.exitCode === 0) {
|
|
isReady = true;
|
|
break;
|
|
}
|
|
|
|
lastOutput = result.output;
|
|
if (!isMissingModuleError(result.output)) {
|
|
throw new Error(`Dev package import probe failed with a non-retryable error:\n${result.output}`);
|
|
}
|
|
|
|
if (!hasLoggedWait) {
|
|
console.log("Waiting for dev package entrypoints to be generated...");
|
|
hasLoggedWait = true;
|
|
}
|
|
await sleep(retryDelayMs);
|
|
}
|
|
|
|
if (!isReady) {
|
|
throw new Error(`Timed out waiting for dev package imports to become available. Last probe output:\n${lastOutput}`);
|
|
}
|
|
}
|
|
|
|
main().then(
|
|
() => undefined,
|
|
(error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
},
|
|
);
|