From a4d43558868ce25bb5891b477048ab10f42cbe0d Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Sun, 21 Jun 2026 14:42:07 -0500 Subject: [PATCH] Try out this build! --- docker/Dockerfile | 2 +- scripts/copy-next-runtime-deps.mjs | 116 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 scripts/copy-next-runtime-deps.mjs diff --git a/docker/Dockerfile b/docker/Dockerfile index e47cb09..7ab2cd2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,6 +41,7 @@ RUN bun install --frozen-lockfile ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production RUN cd apps/next && bun run build:docker +RUN bun scripts/copy-next-runtime-deps.mjs # Runner stage FROM docker.io/library/node:22-alpine AS runner @@ -54,7 +55,6 @@ RUN addgroup --system --gid 1001 nodejs && \ # Copy built application COPY --from=builder --chown=nextjs:nodejs /app/apps/next/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ws ./node_modules/ws COPY --from=builder --chown=nextjs:nodejs /app/apps/next/.next/static ./apps/next/.next/static COPY --from=builder --chown=nextjs:nodejs /app/apps/next/public ./apps/next/public diff --git a/scripts/copy-next-runtime-deps.mjs b/scripts/copy-next-runtime-deps.mjs new file mode 100644 index 0000000..588a516 --- /dev/null +++ b/scripts/copy-next-runtime-deps.mjs @@ -0,0 +1,116 @@ +import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs'; +import { cp } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const rootDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +); +const rootNodeModules = path.join(rootDir, 'node_modules'); +const standaloneNodeModules = path.join( + rootDir, + 'apps/next/.next/standalone/node_modules', +); + +const seedPackages = [ + '@payloadcms/db-postgres', + '@payloadcms/graphql', + '@payloadcms/next', + '@payloadcms/richtext-lexical', + '@payloadcms/ui', + 'payload', +]; + +const packageJsonPathFor = (packageName, startDir) => { + let current = startDir; + + while (true) { + const candidate = path.join( + current, + 'node_modules', + packageName, + 'package.json', + ); + + if (existsSync(candidate)) { + return candidate; + } + + const parent = path.dirname(current); + + if (parent === current) { + return null; + } + + current = parent; + } +}; + +const targetDirFor = (packageName) => + path.join(standaloneNodeModules, ...packageName.split('/')); + +const copyPackage = async (packageName, startDir, seen) => { + if (seen.has(packageName)) { + return; + } + + seen.add(packageName); + + const packageJsonPath = packageJsonPathFor(packageName, startDir); + + if (!packageJsonPath) { + throw new Error(`Unable to resolve runtime package "${packageName}"`); + } + + const packageDir = path.dirname(packageJsonPath); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); + const targetDir = targetDirFor(packageName); + + rmSync(targetDir, { force: true, recursive: true }); + mkdirSync(path.dirname(targetDir), { recursive: true }); + await cp(packageDir, targetDir, { + dereference: false, + errorOnExist: false, + force: true, + recursive: true, + }); + + const dependencies = { + ...packageJson.dependencies, + }; + const optionalDependencies = { + ...packageJson.optionalDependencies, + }; + + for (const dependencyName of [ + ...Object.keys(dependencies).sort(), + ...Object.keys(optionalDependencies).sort(), + ]) { + if (dependencyName.startsWith('@types/')) { + continue; + } + + try { + await copyPackage(dependencyName, packageDir, seen); + } catch (error) { + if (dependencyName in optionalDependencies) { + continue; + } + + throw error; + } + } +}; + +mkdirSync(standaloneNodeModules, { recursive: true }); + +const seen = new Set(); + +for (const packageName of seedPackages) { + await copyPackage(packageName, rootDir, seen); +} + +console.log( + `Copied ${seen.size} Payload runtime packages into Next standalone output.`, +);