# syntax=docker/dockerfile:1
# Next.js production build — standalone output, no source code
FROM node:24-alpine AS base
# Corepack enabled here; each stage that needs pnpm derives from `base`
# and runs `corepack install` after COPY of package.json so the version
# resolves from the packageManager field. No drift between Docker
# builds and host pnpm.
RUN corepack enable

# ── Install dependencies ──
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc* ./
RUN corepack install
COPY apps/ ./apps/
COPY packages/ ./packages/

# Optional pnpm patches/ dir — bind-mount and conditional copy so the
# build doesn't fail when the project doesn't use pnpm patches. Must
# run BEFORE `pnpm install` for patchedDependencies resolution.
RUN --mount=type=bind,source=.,target=/ctx,readonly \
    if [ -d /ctx/patches ]; then \
      mkdir -p ./patches && cp -r /ctx/patches/. ./patches/; \
    fi

RUN pnpm install --frozen-lockfile

# ── Build ──
FROM deps AS builder
WORKDIR /app
# APP_NAME = pnpm workspace package name (e.g., "@numero/web"), for the
# filter selector.  APP_DIR = on-disk directory name under apps/ (e.g.,
# "web"), for COPY paths.  Two values because the package name and the
# directory name aren't necessarily the same in pnpm workspaces.
# ce auto-passes both as build-args (APP_NAME from package.json#name,
# APP_DIR from path.basename(contract.location)).
ARG APP_NAME
ARG APP_DIR
RUN test -n "${APP_NAME}" \
  || (echo "ERROR: APP_NAME build-arg required for nextprod template" && exit 1)
RUN test -n "${APP_DIR}" \
  || (echo "ERROR: APP_DIR build-arg required for nextprod template" && exit 1)

# Build shared library packages first so the Next.js app can import their
# dist/ outputs (and any generated assets like Hardhat ABIs from
# packages/contracts) at build time. Without this, `pnpm --filter <app>
# build` resolves bare-package imports against unbuilt dist/ paths and
# fails with "Module not found". Scoped to ./packages/* — apps are NOT
# built here, just this one. --if-present skips packages without a
# build script. Same pattern as nodeservice.Dockerfile.
RUN pnpm -r --filter "./packages/*" --if-present run build

RUN pnpm --filter "${APP_NAME}" build

# Defensive: ensure apps/${APP_DIR}/public exists before the runner
# stage COPYs from it. Next.js's public/ is developer-authored (not
# generated by `next build`), so apps without static assets simply
# don't have the directory — and the runner's COPY would then fail.
# mkdir -p is idempotent, so this is a no-op when the dir already
# exists with real assets.
RUN mkdir -p apps/${APP_DIR}/public

# ── Production image ──
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

# Build args are stage-scoped — must redeclare to use them in COPY/CMD.
ARG APP_DIR

# Next.js standalone output
COPY --from=builder /app/apps/${APP_DIR}/.next/standalone ./
COPY --from=builder /app/apps/${APP_DIR}/.next/static ./apps/${APP_DIR}/.next/static
COPY --from=builder /app/apps/${APP_DIR}/public ./apps/${APP_DIR}/public

EXPOSE 3000
# CMD doesn't expand ARG/ENV in exec form. Wrap in sh -c to get
# ${APP_DIR} substitution at container start. Without this the server
# never finds server.js (the literal "${APP_DIR}" stays in the path).
CMD ["sh", "-c", "node apps/${APP_DIR}/server.js"]
