diff --git a/packages/stack-cli/scripts/copy-runtime-assets.mjs b/packages/stack-cli/scripts/copy-runtime-assets.mjs index 61d2fec56c..cc222b3cec 100644 --- a/packages/stack-cli/scripts/copy-runtime-assets.mjs +++ b/packages/stack-cli/scripts/copy-runtime-assets.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node import { execFileSync } from "child_process"; -import { chmodSync, cpSync, existsSync, mkdirSync, rmSync } from "fs"; -import { dirname, join, resolve } from "path"; +import { chmodSync, cpSync, existsSync, mkdirSync, readlinkSync, readdirSync, rmSync } from "fs"; +import { dirname, join, relative, resolve } from "path"; import { fileURLToPath } from "url"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -39,6 +39,57 @@ function copyEmulatorAssets() { console.log(`Copied emulator assets into ${emulatorDist} (+ .env.development into ${distDir}).`); } +function shouldCopyDashboardFile(path) { + return existsSync(path); +} + +function copyDashboardSymlinkTarget(src, dest) { + rmSync(dest, { recursive: true, force: true }); + cpSync(src, dest, { recursive: true, dereference: true, filter: shouldCopyDashboardFile }); +} + +function splitDashboardPath(root, path) { + return relative(root, path).split(/[\\/]+/); +} + +function getDashboardDependencyName(pnpmRoot, path) { + const parts = splitDashboardPath(pnpmRoot, path); + const nodeModulesIndex = parts.lastIndexOf("node_modules"); + if (nodeModulesIndex < 0) { + return undefined; + } + const dependencyParts = parts.slice(nodeModulesIndex + 1); + if (dependencyParts.length === 1) { + return dependencyParts[0]; + } + if (dependencyParts.length === 2 && dependencyParts[0].startsWith("@")) { + return join(dependencyParts[0], dependencyParts[1]); + } + return undefined; +} + +function copyDashboardHoistedDependencies(pnpmRoot, current = pnpmRoot) { + for (const entry of readdirSync(current, { withFileTypes: true })) { + const path = join(current, entry.name); + if (entry.isDirectory()) { + copyDashboardHoistedDependencies(pnpmRoot, path); + continue; + } + if (!entry.isSymbolicLink() || !existsSync(path)) { + continue; + } + const dependencyName = getDashboardDependencyName(pnpmRoot, path); + if (dependencyName == null) { + continue; + } + const target = resolve(current, readlinkSync(path)); + const parts = splitDashboardPath(pnpmRoot, path); + if (parts[0] !== "node_modules" && existsSync(join(target, "package.json"))) { + copyDashboardSymlinkTarget(target, join(dashboardDist, "node_modules", dependencyName)); + } + } +} + function copyDashboardAssets() { assertExists( join(dashboardStandaloneSrc, "apps/dashboard/server.js"), @@ -50,11 +101,12 @@ function copyDashboardAssets() { ); rmSync(dashboardDist, { recursive: true, force: true }); - cpSync(dashboardStandaloneSrc, dashboardDist, { recursive: true }); + cpSync(dashboardStandaloneSrc, dashboardDist, { recursive: true, dereference: true, filter: shouldCopyDashboardFile }); cpSync(dashboardStaticSrc, join(dashboardDist, "apps/dashboard/.next/static"), { recursive: true }); if (existsSync(dashboardPublicSrc)) { cpSync(dashboardPublicSrc, join(dashboardDist, "apps/dashboard/public"), { recursive: true }); } + copyDashboardHoistedDependencies(join(dashboardStandaloneSrc, "node_modules/.pnpm")); console.log(`Copied dashboard standalone runtime into ${dashboardDist}.`); }