Skip to content

copyResources skips merging public/ directory in monorepo standalone builds #499

@bill-lunigy

Description

@bill-lunigy

Bug Description

In monorepo (Turborepo) setups with Next.js, the copyResources function in packages/@apphosting/adapter-nextjs/src/utils.ts skips copying the full public/ directory from the app source into the standalone output because a partial public/ directory already exists from Next.js file tracing.

Steps to Reproduce

  1. Create a Turborepo monorepo with a Next.js app in packages/web
  2. Place static assets in packages/web/public/ (e.g., logo.svg, favicons)
  3. Reference these assets via string paths (src="/logo.svg") rather than static imports
  4. Have at least one subdirectory in public/ that IS traced by webpack (e.g., public/legal/ read via fs in server components)
  5. Deploy to Firebase App Hosting with rootDirectory: packages/web

Expected Behavior

All files from packages/web/public/ should be present in the deployed container and served correctly.

Actual Behavior

Only files traced by Next.js standalone (e.g., public/legal/) are present. All other public assets (logo.svg, favicons, other SVGs) return 404.

Root Cause

In utils.ts, the copyResources function uses a top-level existence check:

const existsInOutputBundle = await exists(join(outputBundleAppDir, path));
if (!isbundleYamlDir && !existsInOutputBundle && !isApphostingYaml) {
    await copy(join(appDir, path), join(outputBundleAppDir, path));
}

When Next.js standalone tracing creates a partial public/ directory (containing only traced files like legal/), exists() returns true for the public entry, causing the entire directory copy to be skipped. The function should merge directories rather than skip them.

Suggested Fix

Use fs-extra's copy with { overwrite: false } to merge directories without overwriting existing files:

// Instead of skipping if exists, merge directories
await copy(join(appDir, path), join(outputBundleAppDir, path), { overwrite: false });

Or check at the file level rather than the directory level.

Environment

  • @apphosting/adapter-nextjs: v14.0.21
  • Next.js: 15.x
  • Monorepo: Turborepo
  • Root directory: packages/web
  • Node.js: 22.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions