Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions config/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import fs from "node:fs";

export function resolveAssetsFlagFromEnv() {
return (
(process.env["ASSETS_FROM_S3"] ?? "false").toString().toLowerCase() ===
"true"
);
}

export function loadAssetsFlagAtRuntime() {
let flag = (process.env["ASSETS_FROM_S3"] ?? "").toString().toLowerCase();
if (!flag && fs.existsSync(".next/ASSETS_FROM_S3")) {
flag = fs.readFileSync(".next/ASSETS_FROM_S3", "utf8").trim().toLowerCase();
}
return flag === "true";
}
78 changes: 78 additions & 0 deletions config/nextConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { createSecurityHeaders } from "./securityHeaders";
import { PublicEnv } from "./env.schema";
import { NextConfig } from "next";
import path from "node:path";

export function sharedConfig(
publicEnv: PublicEnv,
assetPrefix: string
): NextConfig {
return {
assetPrefix,
reactCompiler: true,
reactStrictMode: false,
compress: true,
productionBrowserSourceMaps: true,
sassOptions: { quietDeps: true },
experimental: {
webpackMemoryOptimizations: true,
webpackBuildWorker: true,
},
images: {
loader: "default",
remotePatterns: [
{ protocol: "https", hostname: "6529.io" },
{ protocol: "https", hostname: "staging.6529.io" },
{ protocol: "https", hostname: "arweave.net" },
{ protocol: "http", hostname: "localhost" },
{ protocol: "https", hostname: "media.generator.seize.io" },
{ protocol: "https", hostname: "d3lqz0a4bldqgf.cloudfront.net" },
{ protocol: "https", hostname: "i.seadn.io" },
{ protocol: "https", hostname: "i2.seadn.io" },
{ protocol: "https", hostname: "i2c.seadn.io" },
{ protocol: "https", hostname: "res.cloudinary.com" },
{ protocol: "https", hostname: "ipfs.6529.io" },
{ protocol: "https", hostname: "ipfs.io" },
],
minimumCacheTTL: 86400,
formats: ["image/avif", "image/webp"],
qualities: [100, 75],
},
transpilePackages: ["react-tweet"],
poweredByHeader: false,
async headers() {
return [
{
source: "/:path*",
headers: createSecurityHeaders(publicEnv["API_ENDPOINT"]),
},
];
},
webpack: (
config: any,
{ dev, isServer }: { dev: boolean; isServer: boolean }
) => {
config.resolve.alias.canvas = false;
config.resolve.alias.encoding = false;
config.resolve.alias["@react-native-async-storage/async-storage"] = false;
config.resolve.alias["react-native"] = false;
config.resolve.alias["idb-keyval"] = path.resolve(
process.cwd(),
"lib/storage/idb-keyval.ts"
);
if (!dev && !isServer) config.devtool = "source-map";
config.optimization.minimize = false;
return config;
},
turbopack: {
resolveAlias: {
canvas: "./stubs/empty.js",
encoding: "./stubs/empty.js",
"@react-native-async-storage/async-storage": "./stubs/empty.js",
"react-native": "./stubs/empty.js",
"idb-keyval": "./lib/storage/idb-keyval.ts",
},
},
serverExternalPackages: ["@reown/appkit", "@reown/appkit-adapter-wagmi"],
};
}
33 changes: 33 additions & 0 deletions config/runtimeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { publicEnvSchema } from "./env.schema";
import fs from "node:fs";

export function persistBakedArtifacts(
publicEnv: string,
ASSETS_FROM_S3: boolean
) {
try {
fs.mkdirSync(".next", { recursive: true });
fs.writeFileSync(
".next/PUBLIC_RUNTIME.json",
JSON.stringify(publicEnv),
"utf8"
);
fs.writeFileSync(
".next/ASSETS_FROM_S3",
ASSETS_FROM_S3 ? "true" : "false",
"utf8"
);
} catch {}
}
Comment thread
simo6529 marked this conversation as resolved.

export function loadBakedRuntimeConfig(VERSION: string) {
let baked = {};
if (process.env["PUBLIC_RUNTIME"]) {
baked = JSON.parse(process.env["PUBLIC_RUNTIME"]);
} else if (fs.existsSync(".next/PUBLIC_RUNTIME.json")) {
baked = JSON.parse(fs.readFileSync(".next/PUBLIC_RUNTIME.json", "utf8"));
}
const parsed = publicEnvSchema.safeParse({ ...baked, VERSION });
if (!parsed.success) throw parsed.error; // FAIL-FAST
return parsed.data;
}
2 changes: 1 addition & 1 deletion config/securityHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function createSecurityHeaders(apiEndpoint = "") {
export function createSecurityHeaders(apiEndpoint: string | undefined = "") {
return [
{
key: "Strict-Transport-Security",
Expand Down
25 changes: 25 additions & 0 deletions config/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { execSync } from "node:child_process";

export function logOnceConfig(label: string, message: string) {
if (!process.env[`__LOG_${label}_ONCE__`]) {
process.env[`__LOG_${label}_ONCE__`] = "1";
process.env["__LOG_ENV_ONCE__"] = "1";
console.log(`${label}: ${message}`);
}
}

export function computeVersionFromEnvOrGit() {
let VERSION = process.env["VERSION"];
if (VERSION) {
logOnceConfig("VERSION (explicit)", VERSION);
return VERSION;
}
try {
VERSION = execSync("git rev-parse HEAD").toString().trim();
logOnceConfig("VERSION (from git HEAD)", VERSION);
} catch {
VERSION = "6529seize";
logOnceConfig("VERSION (default)", VERSION);
}
return VERSION;
}
165 changes: 17 additions & 148 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,165 +5,34 @@ import {
PHASE_PRODUCTION_BUILD,
PHASE_PRODUCTION_SERVER,
} from "next/constants.js";
import { execSync } from "node:child_process";

import fs from "node:fs";
import path from "node:path";

import { createRequire } from "node:module";
import { createSecurityHeaders } from "@/config/securityHeaders";

import { NextConfig } from "next";
import { computeVersionFromEnvOrGit, logOnceConfig } from "@/config/version";
import {
loadAssetsFlagAtRuntime,
resolveAssetsFlagFromEnv,
} from "@/config/assets";
import {
loadBakedRuntimeConfig,
persistBakedArtifacts,
} from "@/config/runtimeConfig";
import { sharedConfig } from "@/config/nextConfig";
const require = createRequire(import.meta.url);
const sentryEnabled = Boolean(process.env["SENTRY_DSN"]);

function logOnce(label: string, message: string) {
if (!process.env[`__LOG_${label}_ONCE__`]) {
process.env[`__LOG_${label}_ONCE__`] = "1";
process.env["__LOG_ENV_ONCE__"] = "1";
console.log(`${label}: ${message}`);
}
}

// ───────
// Helpers
// ───────
const schemaMod = require("./config/env.schema.runtime.cjs");
const { publicEnvSchema } = schemaMod;

function computeVersionFromEnvOrGit() {
let VERSION = process.env["VERSION"];
if (VERSION) {
logOnce("VERSION (explicit)", VERSION);
return VERSION;
}
try {
VERSION = execSync("git rev-parse HEAD").toString().trim();
logOnce("VERSION (from git HEAD)", VERSION);
} catch {
VERSION = "6529seize";
logOnce("VERSION (default)", VERSION);
}
return VERSION;
}

function resolveAssetsFlagFromEnv() {
return (
(process.env["ASSETS_FROM_S3"] ?? "false").toString().toLowerCase() ===
"true"
);
}

function persistBakedArtifacts(publicEnv: string, ASSETS_FROM_S3: boolean) {
try {
fs.mkdirSync(".next", { recursive: true });
fs.writeFileSync(
".next/PUBLIC_RUNTIME.json",
JSON.stringify(publicEnv),
"utf8"
);
fs.writeFileSync(
".next/ASSETS_FROM_S3",
ASSETS_FROM_S3 ? "true" : "false",
"utf8"
);
} catch {}
}

function loadBakedRuntimeConfig(VERSION: string) {
let baked = {};
if (process.env["PUBLIC_RUNTIME"]) {
baked = JSON.parse(process.env["PUBLIC_RUNTIME"]);
} else if (fs.existsSync(".next/PUBLIC_RUNTIME.json")) {
baked = JSON.parse(fs.readFileSync(".next/PUBLIC_RUNTIME.json", "utf8"));
}
const parsed = publicEnvSchema.safeParse({ ...baked, VERSION });
if (!parsed.success) throw parsed.error; // FAIL-FAST
return parsed.data;
}

function loadAssetsFlagAtRuntime() {
let flag = (process.env["ASSETS_FROM_S3"] ?? "").toString().toLowerCase();
if (!flag && fs.existsSync(".next/ASSETS_FROM_S3")) {
flag = fs.readFileSync(".next/ASSETS_FROM_S3", "utf8").trim().toLowerCase();
}
return flag === "true";
}

function sharedConfig(
publicEnv: Record<string, string>,
assetPrefix: string
): NextConfig {
return {
assetPrefix,
reactCompiler: true,
reactStrictMode: false,
compress: true,
productionBrowserSourceMaps: true,
sassOptions: { quietDeps: true },
experimental: {
webpackMemoryOptimizations: true,
webpackBuildWorker: true,
},
images: {
loader: "default",
remotePatterns: [
{ protocol: "https", hostname: "6529.io" },
{ protocol: "https", hostname: "staging.6529.io" },
{ protocol: "https", hostname: "arweave.net" },
{ protocol: "http", hostname: "localhost" },
{ protocol: "https", hostname: "media.generator.seize.io" },
{ protocol: "https", hostname: "d3lqz0a4bldqgf.cloudfront.net" },
{ protocol: "https", hostname: "i.seadn.io" },
{ protocol: "https", hostname: "i2.seadn.io" },
{ protocol: "https", hostname: "i2c.seadn.io" },
{ protocol: "https", hostname: "res.cloudinary.com" },
{ protocol: "https", hostname: "ipfs.6529.io" },
{ protocol: "https", hostname: "ipfs.io" },
],
minimumCacheTTL: 86400,
formats: ["image/avif", "image/webp"],
qualities: [100, 75],
},
transpilePackages: ["react-tweet"],
poweredByHeader: false,
async headers() {
return [
{
source: "/:path*",
headers: createSecurityHeaders(publicEnv["API_ENDPOINT"]),
},
];
},
webpack: (
config: any,
{ dev, isServer }: { dev: boolean; isServer: boolean }
) => {
config.resolve.alias.canvas = false;
config.resolve.alias.encoding = false;
config.resolve.alias["@react-native-async-storage/async-storage"] = false;
config.resolve.alias["react-native"] = false;
config.resolve.alias["idb-keyval"] = path.resolve(
process.cwd(),
"lib/storage/idb-keyval.ts"
);
if (!dev && !isServer) config.devtool = "source-map";
config.optimization.minimize = false;
return config;
},
turbopack: {
resolveAlias: {
canvas: "./stubs/empty.js",
encoding: "./stubs/empty.js",
"@react-native-async-storage/async-storage": "./stubs/empty.js",
"react-native": "./stubs/empty.js",
"idb-keyval": "./lib/storage/idb-keyval.ts",
},
},
serverExternalPackages: ["@reown/appkit", "@reown/appkit-adapter-wagmi"],
};
}

const nextConfigFactory = (phase: string): NextConfig => {
const mode = process.env.NODE_ENV;
logOnce("NODE_ENV", mode);
logOnceConfig("NODE_ENV", mode);

// Build & Dev phases
if (phase === PHASE_DEVELOPMENT_SERVER || phase === PHASE_PRODUCTION_BUILD) {
Expand All @@ -172,7 +41,7 @@ const nextConfigFactory = (phase: string): NextConfig => {

const VERSION = computeVersionFromEnvOrGit();
const ASSETS_FROM_S3 = resolveAssetsFlagFromEnv();
logOnce("ASSETS_FROM_S3", ASSETS_FROM_S3.toString());
logOnceConfig("ASSETS_FROM_S3", ASSETS_FROM_S3.toString());

// Prepare and validate public runtime from process.env
const shape = publicEnvSchema._def.shape();
Expand Down Expand Up @@ -238,11 +107,11 @@ const nextConfigFactory = (phase: string): NextConfig => {
// Production server phase
if (phase === PHASE_PRODUCTION_SERVER) {
const VERSION = fs.readFileSync(".next/BUILD_ID", "utf8").trim();
logOnce("VERSION (from BUILD_ID)", VERSION);
logOnceConfig("VERSION (from BUILD_ID)", VERSION);

const publicEnv = loadBakedRuntimeConfig(VERSION); // FAIL-FAST inside
const ASSETS_FROM_S3 = loadAssetsFlagAtRuntime();
logOnce("ASSETS_FROM_S3", ASSETS_FROM_S3.toString());
logOnceConfig("ASSETS_FROM_S3", ASSETS_FROM_S3.toString());

const assetPrefix = ASSETS_FROM_S3
? `https://dnclu2fna0b2b.cloudfront.net/web_build/${VERSION}`
Expand Down