From 80be421d889b9acc89f1f6133565bbfe58c2f429 Mon Sep 17 00:00:00 2001 From: Abhinav Sundaresan Date: Tue, 10 Nov 2026 00:33:02 +0000 Subject: [PATCH] fix(adapter-nextjs): don't enable browser source maps for Crashlytics until upload is supported With CRASHLYTICS_UPLOAD_MAPPING=true, modifyConfig set productionBrowserSourceMaps: true. Nothing on Cloud Run uploads those maps to Crashlytics or removes them from the image yet, so /_next/static/chunks/*.js.map was served publicly, with the full sourcesContent and an immutable one-year Cache-Control. modifyConfig now ignores CRASHLYTICS_UPLOAD_MAPPING, logs one warning that says why, and never changes productionBrowserSourceMaps, so a user's own setting is kept. A TODO marks where to turn it back on once source maps are uploaded and stripped during the build. --- .../@google-cloud/adapter-nextjs/README.md | 4 +- .../adapter-nextjs/src/adapter.spec.ts | 42 +++++++++++++++---- .../src/adapter/modify-config.ts | 21 ++++------ 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/packages/@google-cloud/adapter-nextjs/README.md b/packages/@google-cloud/adapter-nextjs/README.md index 74e5c4d7f..566f14707 100644 --- a/packages/@google-cloud/adapter-nextjs/README.md +++ b/packages/@google-cloud/adapter-nextjs/README.md @@ -17,7 +17,7 @@ Runs only during `phase-production-build`: - **Standalone output**: Sets `output: 'standalone'` when `config.output` is unset (preserves `output: 'export'` and explicit `output: 'standalone'`). - **Build cache**: Always sets `experimental.turbopackFileSystemCacheForBuild = false`. Cloud Run never restores `.next/cache`, so this avoids ~48 MiB of unused cache in container layers. - **Image optimization**: Sets `images.unoptimized: true`, so Next.js image optimization is off by default. Apps with a custom image loader, and builds that set `GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true`, keep it on. See [Image Optimization](#image-optimization). -- **Browser source maps**: Always sets `productionBrowserSourceMaps: true` when `CRASHLYTICS_UPLOAD_MAPPING=true` (or `"1"`), regardless of any user value in `next.config`. +- **Browser source maps**: Never changes `productionBrowserSourceMaps`. `CRASHLYTICS_UPLOAD_MAPPING=true` (or `"1"`) is ignored for now and logs a warning: Crashlytics source-map upload isn't available on Cloud Run yet, and turning on browser source maps would serve the app's source publicly from `/_next/static`. ### `onBuildComplete(ctx)` @@ -102,7 +102,7 @@ outputFiles: | :--------------------------------- | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NEXT_ADAPTER_PATH` | Build (`next build`) | Absolute path to `@google-cloud/adapter-nextjs` (`dist/index.js`) used by Next.js to load the adapter. | | `NEXT_PRIVATE_OUTPUT_TRACE_ROOT` | Build (`next build`) | Repository/workspace root (`/workspace` on Cloud Run). Used as `repoRoot` for relative paths and `.cloudrun/` output placement. | -| `CRASHLYTICS_UPLOAD_MAPPING` | Build (`next build`) | When set to `"true"` (or `"1"`), sets `productionBrowserSourceMaps: true` in `next.config`. | +| `CRASHLYTICS_UPLOAD_MAPPING` | Build (`next build`) | Ignored for now (logs a warning). It doesn't turn on `productionBrowserSourceMaps`; see **Browser source maps** above. | | `GOOGLE_NEXTJS_IMAGE_OPTIMIZATION` | Build (`next build`) | When set to `"true"` (or `"1"`), keeps Next.js image optimization on instead of setting `images.unoptimized: true`. See [Image Optimization](#image-optimization). | | `PORT` | Runtime (`serve.js`) | HTTP port for the server. Cloud Run always sets it; defaults to `3000` when unset. | | `KEEP_ALIVE_TIMEOUT` | Runtime (`serve.js`) | Optional HTTP server `keepAliveTimeout` in milliseconds. | diff --git a/packages/@google-cloud/adapter-nextjs/src/adapter.spec.ts b/packages/@google-cloud/adapter-nextjs/src/adapter.spec.ts index 18bcffcb5..329e5e933 100644 --- a/packages/@google-cloud/adapter-nextjs/src/adapter.spec.ts +++ b/packages/@google-cloud/adapter-nextjs/src/adapter.spec.ts @@ -256,18 +256,42 @@ describe("@google-cloud/adapter-nextjs", () => { assert.ok(infoLogs.some((l) => l.includes('already has output: "standalone"; preserving'))); }); - it("enables productionBrowserSourceMaps only when CRASHLYTICS_UPLOAD_MAPPING=true", () => { - process.env.CRASHLYTICS_UPLOAD_MAPPING = "true"; - const inputConfig = deepFreeze({}); - const result = modifyConfig(inputConfig, { + it("ignores CRASHLYTICS_UPLOAD_MAPPING and never changes productionBrowserSourceMaps", () => { + const ctx: ModifyConfigContext = { phase: "phase-production-build", nextVersion: "16.4.3", projectDir: tempDir, - }); - assert.strictEqual(result.productionBrowserSourceMaps, true); - assert.ok( - infoLogs.some((l) => l.includes("Enabling NextConfig productionBrowserSourceMaps=true")), - ); + }; + const ignoredLogs = (): string[] => + warnLogs.filter((w) => w.includes("Ignoring CRASHLYTICS_UPLOAD_MAPPING=true")); + + for (const value of ["true", "1"]) { + process.env.CRASHLYTICS_UPLOAD_MAPPING = value; + warnLogs = []; + const result = modifyConfig(deepFreeze({}), ctx); + assert.strictEqual(result.productionBrowserSourceMaps, undefined); + assert.strictEqual(ignoredLogs().length, 1); + assert.ok(ignoredLogs()[0].includes("would serve the app's source publicly")); + } + + // A user's own setting is left as is, whether or not the env var is set. + for (const userValue of [true, false]) { + for (const envValue of ["true", undefined]) { + if (envValue === undefined) { + delete process.env.CRASHLYTICS_UPLOAD_MAPPING; + } else { + process.env.CRASHLYTICS_UPLOAD_MAPPING = envValue; + } + const result = modifyConfig(deepFreeze({ productionBrowserSourceMaps: userValue }), ctx); + assert.strictEqual(result.productionBrowserSourceMaps, userValue); + } + } + + warnLogs = []; + delete process.env.CRASHLYTICS_UPLOAD_MAPPING; + modifyConfig(deepFreeze({}), ctx); + assert.strictEqual(ignoredLogs().length, 0); + assert.ok(!infoLogs.some((l) => l.includes("productionBrowserSourceMaps"))); }); it("logs security warnings for 16.3.5 and 16.3.7 without blocking", () => { diff --git a/packages/@google-cloud/adapter-nextjs/src/adapter/modify-config.ts b/packages/@google-cloud/adapter-nextjs/src/adapter/modify-config.ts index ec882d6ae..87ed5d5dc 100644 --- a/packages/@google-cloud/adapter-nextjs/src/adapter/modify-config.ts +++ b/packages/@google-cloud/adapter-nextjs/src/adapter/modify-config.ts @@ -32,7 +32,8 @@ function isEnvFlagEnabled(name: string): boolean { * - Sets `images.unoptimized: true` (image optimization off by default) unless the config uses a * custom image loader (`images.loader` / `images.loaderFile`) or * `GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true`, and logs which case applied. - * - Enables `productionBrowserSourceMaps: true` only when `CRASHLYTICS_UPLOAD_MAPPING=true`. + * - Never changes `productionBrowserSourceMaps`. `CRASHLYTICS_UPLOAD_MAPPING=true` is ignored with a + * warning until Crashlytics source-map upload is available on Cloud Run. * - Never mutates the input `config` object in place, and never throws. */ export function modifyConfig( @@ -137,18 +138,14 @@ export function modifyConfig( ); } + // TODO(b/578288818): Turn productionBrowserSourceMaps back on for CRASHLYTICS_UPLOAD_MAPPING once + // the Cloud Run source-map buildpack uploads and strips the maps (e.g. only when a build signal + // says the uploader will run). Until then, enabling them would serve the app's source publicly + // from /_next/static, so the env var is ignored and a user's own setting is left as is. if (isEnvFlagEnabled("CRASHLYTICS_UPLOAD_MAPPING")) { - if (config.productionBrowserSourceMaps !== true) { - const target = ensureCopy(); - target.productionBrowserSourceMaps = true; - console.log( - "[adapter-nextjs] Enabling NextConfig productionBrowserSourceMaps=true because CRASHLYTICS_UPLOAD_MAPPING=true.", - ); - } else { - console.log( - "[adapter-nextjs] NextConfig productionBrowserSourceMaps is already true (CRASHLYTICS_UPLOAD_MAPPING=true).", - ); - } + console.warn( + "[adapter-nextjs] Warning: Ignoring CRASHLYTICS_UPLOAD_MAPPING=true: Crashlytics source-map upload isn't available on Cloud Run yet, and enabling browser source maps would serve the app's source publicly. NextConfig productionBrowserSourceMaps is left unchanged.", + ); } return modified ?? config;