everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
187 lines (185 loc) • 8.33 kB
JavaScript
const require_fs = require('../fs.cjs');
let effect = require("effect");
//#region src/services/scaffold/upgrade-audit/steps/migrate-pnpm.ts
const V2_CATALOG = {
"everything-dev": "^2.0.0-rc.14",
"every-plugin": "^3.0.0-rc.9",
"better-near-auth": "^2.0.0-rc.2",
effect: "4.0.0-rc.117",
zod: "4.6.5",
"better-auth": "1.7.7",
"@better-auth/api-key": "1.7.7",
"@better-auth/passkey": "1.7.7",
"@better-auth/core": "1.7.7",
"@orpc/contract": "2.0.0-beta.35",
"@orpc/server": "2.0.0-beta.35",
"@orpc/client": "2.0.0-beta.35",
"@orpc/zod": "2.0.0-beta.35",
"@orpc/openapi": "2.0.0-beta.35",
"@orpc/tanstack-query": "2.0.0-beta.35",
"@orpc/publisher": "2.0.0-beta.35",
"@orpc/experimental-effect": "2.0.0-beta.35",
"@tanstack/react-query": "5.104.5",
"@tanstack/react-query-devtools": "^5.104.5",
"@tanstack/react-router": "1.170.45",
"@tanstack/react-router-devtools": "1.167.2",
"@tanstack/router-generator": "1.167.45",
"@tanstack/router-plugin": "1.168.47",
"@tanstack/react-devtools": "^0.10.13",
"@tanstack/react-table": "^9.3.1",
"near-kit": "^0.20.2",
"@module-federation/enhanced": "^2.9.2",
"@module-federation/node": "^2.7.52",
"@module-federation/rsbuild-plugin": "^2.9.2",
"@module-federation/runtime-core": "^2.9.2",
"@module-federation/runtime": "^2.9.2",
"@module-federation/runtime-tools": "^2.9.2",
"@module-federation/sdk": "^2.9.2",
"@module-federation/dts-plugin": "^2.9.2",
"@rspack/core": "2.2.9",
"@rspack/cli": "2.2.9",
"@rsbuild/core": "2.2.14",
"@rsbuild/plugin-react": "2.1.1",
typescript: "^7.0.2"
};
const MINIMUM_RELEASE_AGE_EXCLUDE = [
"better-near-auth@2.0.0-rc.0 || 2.0.0-rc.1 || 2.0.0-rc.2",
"every-plugin@3.0.0-rc.4 || 3.0.0-rc.5 || 3.0.0-rc.6 || 3.0.0-rc.7 || 3.0.0-rc.8 || 3.0.0-rc.9",
"everything-dev@2.0.0-rc.7 || 2.0.0-rc.8 || 2.0.0-rc.9 || 2.0.0-rc.10 || 2.0.0-rc.11 || 2.0.0-rc.12 || 2.0.0-rc.13 || 2.0.0-rc.14"
];
const PNPM_PACKAGE_MANAGER = "pnpm@12.10.1";
/**
* Transitive dedupe pins. @tanstack/react-router-devtools drags in a
* different @tanstack/router-core than @tanstack/react-router resolves, so
* everything-dev's dist (which imports the hoisted core) disagrees with the
* app-typed RouterCore — every AppRouterFactory assignment fails with
* missing `routeBranchCache`/`resolvePathWithBase`/`getRouteBranch`, and the
* stale react-router variant breaks `bos types gen` at import time
* (`isPlainArray` no longer exported). Force one core/router pair
* everywhere; bump in lockstep with the react-router catalog pin.
*/
const TRANSITIVE_OVERRIDES = {
"@tanstack/router-core": "1.171.38",
"@tanstack/react-router": "1.170.45"
};
const YAML_PLAIN_VALUE_RE = /^[A-Za-z0-9^~][A-Za-z0-9.^~*+\-/]*$/;
const yamlScalar = (value) => YAML_PLAIN_VALUE_RE.test(value) ? value : JSON.stringify(value);
const yamlMap = (entries, indent) => entries.map(([key, value]) => /^[A-Za-z0-9_.-]+$/.test(key) ? `${indent}${key}: ${yamlScalar(value)}` : `${indent}${JSON.stringify(key)}: ${yamlScalar(value)}`).join("\n");
const renderWorkspaceYaml = (input) => {
const catalogEntries = Object.entries(input.catalog).map(([key, value]) => [key, value]);
return [
"packages:",
...input.packages.map((pkg) => ` - ${yamlScalar(pkg)}`),
"",
"allowBuilds:",
" esbuild: true",
" sharp: true",
" protobufjs: true",
" '@openapitools/openapi-generator-cli': true",
"",
"overrides:",
...Object.entries(TRANSITIVE_OVERRIDES).map(([key, value]) => ` ${yamlMap([[key, value]], "").trimStart()}`),
"",
"catalog:",
yamlMap(catalogEntries, " "),
"",
"minimumReleaseAgeExclude:",
...MINIMUM_RELEASE_AGE_EXCLUDE.map((entry) => ` - ${yamlScalar(entry)}`),
""
].join("\n");
};
const PLUGIN_TYPECHECK_LOOP = "for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then pnpm --dir \"$dir\" run typecheck || exit 1; fi; done";
const PLUGIN_TEST_LOOP = "for d in plugins/*; do if [ -f \"$d/package.json\" ]; then pnpm --dir \"$d\" --if-present run test || exit 1; fi; done";
const hardenedTypecheck = (dirs) => [
"pnpm run types:gen",
...dirs.map((dir) => `if [ -d ${dir} ]; then pnpm --dir ${dir} run typecheck || exit 1; fi`),
PLUGIN_TYPECHECK_LOOP
].join(" && ");
const hardenedTest = (dirs) => [...dirs.map((dir) => `pnpm --dir ${dir} --if-present run test || exit 1`), `[ -d plugins ] && ${PLUGIN_TEST_LOOP} || true`].join(" && ");
/** Mechanical bun→pnpm rewrites, safe for any workspace's scripts. */
const mechanicalScriptRewrite = (value) => value.replaceAll("bun run --if-present --cwd ", "pnpm --dir ").replaceAll("bun run --cwd ", "pnpm --dir ").replace(/bun run --cwd (\S+) (.+)/, "pnpm --dir $1 exec $2").replaceAll("bun run ", "pnpm run ").replace(/node node_modules\/\.bin\/bos /, "bos ").replace("for d in plugins/*; do [ -f \"$d/package.json\" ] && bun run --if-present --cwd \"$d\" test; done", "for d in plugins/*; do if [ -f \"$d/package.json\" ]; then pnpm --dir \"$d\" --if-present run test || exit 1; fi; done").replace("for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then bun run --cwd \"$dir\" typecheck; fi; done", "for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then pnpm --dir \"$dir\" run typecheck || exit 1; fi; done");
const rewriteScripts = (scripts) => {
const next = {};
for (const [key, value] of Object.entries(scripts)) {
if (key === "typecheck") {
next[key] = hardenedTypecheck(["ui", "api"]);
continue;
}
if (key === "test") {
next[key] = hardenedTest(["api", "ui"]);
continue;
}
next[key] = mechanicalScriptRewrite(value);
}
return next;
};
const WORKSPACE_PKG_GLOB = "{ui/package.json,api/package.json,plugins/*/package.json}";
/**
* Workspace package.json scripts still speak bun (`bun run build:client`,
* `bun run --cwd api typecheck`). Rewrite every bun invocation to the pnpm
* form so the pnpm-only tree (packageManager pin, no bun in CI) works.
* Unlike the root pass, workspace typecheck/test scripts keep their own
* bodies — only the bun invocations are mechanically rewritten.
*/
const rewriteWorkspaceScripts = (projectDir, dryRun) => effect.Effect.gen(function* () {
const pkgPaths = yield* require_fs.listFiles(projectDir, WORKSPACE_PKG_GLOB);
const changed = [];
for (const pkgPath of pkgPaths) {
const source = yield* require_fs.readText(projectDir, pkgPath);
if (!source.includes("bun ")) continue;
const pkg = JSON.parse(source);
if (!pkg.scripts) continue;
const scripts = Object.fromEntries(Object.entries(pkg.scripts).map(([key, value]) => [key, mechanicalScriptRewrite(value)]));
if (JSON.stringify(scripts) === JSON.stringify(pkg.scripts)) continue;
if (!dryRun) yield* require_fs.writeText(projectDir, pkgPath, `${JSON.stringify({
...pkg,
scripts
}, null, 2)}\n`);
changed.push(pkgPath);
}
return changed;
});
const BUN_ARTIFACTS = ["bun.lock", "bunfig.toml"];
const RETIRED_FILES = ["railway.toml", "Dockerfile"];
const migratePnpm = (projectDir, dryRun) => effect.Effect.gen(function* () {
const pkg = JSON.parse(yield* require_fs.readText(projectDir, "package.json"));
const catalog = {
...pkg.workspaces?.catalog ?? {},
...V2_CATALOG
};
const packages = pkg.workspaces?.packages ?? [
"api",
"ui",
"plugins/*"
];
const workspaceYaml = renderWorkspaceYaml({
packages,
catalog
});
const nextPkg = {
...pkg,
packageManager: PNPM_PACKAGE_MANAGER,
scripts: rewriteScripts(pkg.scripts ?? {}),
workspaces: void 0
};
const writes = ["pnpm-workspace.yaml", "package.json"];
const deletes = [...BUN_ARTIFACTS, ...RETIRED_FILES];
const notes = ["catalog now lives in pnpm-workspace.yaml; bos upgrade syncs framework versions from the parent", "Dockerfile + railway.toml retired — deploys use ci.image (the upstream runtime image)"];
const workspaceScripts = yield* rewriteWorkspaceScripts(projectDir, dryRun);
if (dryRun) return {
writes: [...writes, ...workspaceScripts],
deletes,
notes
};
yield* require_fs.writeText(projectDir, "pnpm-workspace.yaml", workspaceYaml);
yield* require_fs.writeText(projectDir, "package.json", `${JSON.stringify(nextPkg, null, 2)}\n`);
for (const path of [...BUN_ARTIFACTS, ...RETIRED_FILES]) yield* require_fs.removePath(projectDir, path);
return {
writes: [...writes, ...workspaceScripts],
deletes,
notes
};
});
//#endregion
exports.migratePnpm = migratePnpm;
//# sourceMappingURL=migrate-pnpm.cjs.map