From ed321fe990047ee6542a5ae91618aa49ee2f453a Mon Sep 17 00:00:00 2001 From: Jingles Date: Sun, 10 May 2026 16:22:46 +0800 Subject: [PATCH] fix(build): lazy-init mainnet provider; remove global sideEffects:false Two interacting issues caused the production build to crash on routes that imported resolve-adahandle (notably /wallets/[wallet]/transactions/new): 1. resolve-adahandle.tsx called getProvider(1) at module top level. Under `next build` page-data collection, every server-rendered page imports that module, which constructs BlockfrostProvider(undefined) when NEXT_PUBLIC_BLOCKFROST_API_KEY_MAINNET isn't readable at build time (e.g. SKIP_ENV_VALIDATION=true). The constructor throws and the webpack runtime reports it as a generic "factory error". 2. next.config.js had `optimization.sideEffects: false` set globally, which tells webpack that *every* file is side-effect-free. That silently strips global CSS imports and any module-level initialization, masking issues like (1) until you hit a route that exercises them. Fix: - Lazy-init the mainnet provider with a cached singleton, so import is free and instantiation only happens when a caller actually resolves a handle (always client-side). - Remove the global sideEffects:false override. Per-package sideEffects declarations in package.json are the correct mechanism; the global override was masking real bugs. - Move swagger-ui-react CSS import from api-docs.tsx into _app.tsx so Next.js Pages Router's "global CSS only from _app" rule is satisfied. Verified locally: `next build --webpack` completes; both /wallets/[wallet]/transactions/new and /api-docs render. Co-Authored-By: Claude Opus 4.7 (1M context) --- next.config.js | 9 ++++++--- .../common/cardano-objects/resolve-adahandle.tsx | 12 +++++++++--- src/pages/_app.tsx | 1 + src/pages/api-docs.tsx | 3 ++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/next.config.js b/next.config.js index 32444230..67efb015 100644 --- a/next.config.js +++ b/next.config.js @@ -55,13 +55,16 @@ const config = { layers: true, }; - // Optimize tree-shaking by ensuring proper module resolution + // Optimize tree-shaking by ensuring proper module resolution. + // Note: do NOT set `sideEffects: false` globally — it tells webpack that + // every file is side-effect-free, which silently strips CSS imports, + // polyfills, and other modules that exist purely for their side effects. + // Per-package sideEffects flags in package.json are the correct surface. config.optimization = { ...config.optimization, usedExports: true, - sideEffects: false, }; - + // Handle CommonJS modules that don't support named exports config.resolve = { ...config.resolve, diff --git a/src/components/common/cardano-objects/resolve-adahandle.tsx b/src/components/common/cardano-objects/resolve-adahandle.tsx index d1ba72d8..51ee040b 100644 --- a/src/components/common/cardano-objects/resolve-adahandle.tsx +++ b/src/components/common/cardano-objects/resolve-adahandle.tsx @@ -1,8 +1,14 @@ import { toast } from "@/hooks/use-toast"; import { getProvider } from "@/utils/get-provider"; -//AdaHandle look up provider only supports mainnnet -const provider = getProvider(1) +// AdaHandle lookup is mainnet-only. Lazy-init the provider so the module +// can be imported during SSR / page-data collection without requiring +// NEXT_PUBLIC_BLOCKFROST_API_KEY_MAINNET to be defined at module-load time. +let cachedProvider: ReturnType | null = null; +const getMainnetProvider = () => { + if (!cachedProvider) cachedProvider = getProvider(1); + return cachedProvider; +}; export const resolveAdaHandle = async ( setAdaHandle: (value: string) => void, @@ -18,7 +24,7 @@ export const resolveAdaHandle = async ( return; } - const address = await provider.fetchHandleAddress(handleName); + const address = await getMainnetProvider().fetchHandleAddress(handleName); if (address) { const newAddresses = [...recipientAddresses]; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 903d99bb..774fb03c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -13,6 +13,7 @@ import { env } from "@/env"; import { api } from "@/utils/api"; import "@/styles/globals.css"; +import "swagger-ui-react/swagger-ui.css"; import "@/styles/swagger-overrides.css"; import { Toaster } from "@/components/ui/toaster"; import Metatags from "@/components/ui/metatags"; diff --git a/src/pages/api-docs.tsx b/src/pages/api-docs.tsx index 7c97eb78..495c867c 100644 --- a/src/pages/api-docs.tsx +++ b/src/pages/api-docs.tsx @@ -6,8 +6,9 @@ import { Key, Lightbulb, Copy, Check } from "lucide-react"; import Globe from "./globe"; // Avoid SSR for Swagger UI +// Note: swagger-ui CSS is imported globally from src/pages/_app.tsx because +// Next.js Pages Router only allows global CSS imports from the custom App. const SwaggerUI = dynamic(() => import("swagger-ui-react"), { ssr: false }); -import "swagger-ui-react/swagger-ui.css"; export const getServerSideProps = () => ({ props: {} });