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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions