@@ -53,7 +53,7 @@ const Footer = ({ className, ...rest }: FooterProps) => {
.{" "}
& {
@@ -14,10 +14,10 @@ const Time = ({ date, format: formatStr = "PPpp", ...rest }: TimeProps) => {
return (
);
};
diff --git a/lib/config/constants.ts b/lib/config/constants.ts
index cb02c029..7c3e1cb7 100644
--- a/lib/config/constants.ts
+++ b/lib/config/constants.ts
@@ -1,16 +1,3 @@
-/**
- * Locale code to define the site's language in ISO-639 format. Defaults to `en-US`.
- * @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table
- */
-export const SITE_LOCALE = "en-US";
-
-/**
- * Consistent timezone for the site. Doesn't really matter what it is, as long as it's the same everywhere to avoid
- * hydration complaints.
- * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
- */
-export const SITE_TZ = "America/New_York";
-
/** Path to directory with .mdx files, relative to project root. */
export const POSTS_DIR = "notes";
@@ -22,9 +9,3 @@ export const AVATAR_PATH = "app/avatar.jpg";
/** Maximum width of content wrapper (e.g. for images) in pixels. */
export const MAX_WIDTH = 865;
-
-/** Chooses the most appropriate URL for the current deployment. Defined in [`next.config.ts`](../../next.config.ts). */
-export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL!;
-
-/** Freezes time at build. Defined in [`next.config.ts`](../../next.config.ts). */
-export const RELEASE_TIMESTAMP = process.env.NEXT_PUBLIC_RELEASE_TIMESTAMP!;
diff --git a/lib/config/index.ts b/lib/config/index.ts
index e48bf4ea..3cda4dfd 100644
--- a/lib/config/index.ts
+++ b/lib/config/index.ts
@@ -7,7 +7,6 @@ export const license = "Creative Commons Attribution 4.0 International";
export const licenseAbbr = "CC-BY-4.0";
export const licenseUrl = "https://creativecommons.org/licenses/by/4.0/";
export const copyrightYearStart = 2001;
-export const githubRepo = "jakejarvis/jarv.is";
// Me info
export const authorName = "Jake Jarvis";
diff --git a/lib/env.ts b/lib/env.ts
index d72336c1..be175576 100644
--- a/lib/env.ts
+++ b/lib/env.ts
@@ -64,6 +64,13 @@ export const env = createEnv({
TURNSTILE_SECRET_KEY: v.optional(v.string(), "1x0000000000000000000000000000000AA"),
},
client: {
+ /**
+ * Optional. Overrides the most appropriate default URL for the current deployment.
+ *
+ * @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value
+ */
+ NEXT_PUBLIC_BASE_URL: v.optional(v.pipe(v.string(), v.url())),
+
/**
* Optional. Enables comments on blog posts via GitHub discussions.
*
@@ -77,6 +84,11 @@ export const env = createEnv({
*/
NEXT_PUBLIC_GISCUS_REPO_ID: v.optional(v.string()),
+ /**
+ * Required. GitHub repository for the site in the format of `{username}/{repo}`.
+ */
+ NEXT_PUBLIC_GITHUB_REPO: v.string(),
+
/**
* Optional. Sets an `Onion-Location` header in responses to advertise a URL for the same page but hosted on a
* hidden service on the Tor network. Browsers like Brave and Tor Browser will automatically pick this up and offer
@@ -86,6 +98,21 @@ export const env = createEnv({
*/
NEXT_PUBLIC_ONION_DOMAIN: v.optional(v.pipe(v.string(), v.endsWith(".onion"))),
+ /**
+ * Optional. Locale code to define the site's language in ISO-639 format. Defaults to `en-US`.
+ *
+ * @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table
+ */
+ NEXT_PUBLIC_SITE_LOCALE: v.optional(v.string(), "en-US"),
+
+ /**
+ * Optional. Consistent timezone for the site. Doesn't really matter what it is, as long as it's the same everywhere
+ * to avoid hydration complaints.
+ *
+ * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
+ */
+ NEXT_PUBLIC_SITE_TZ: v.optional(v.string(), "America/New_York"),
+
/**
* Required. Site key must be prefixed with NEXT_PUBLIC_ since it is used to embed the captcha widget. Falls back to
* testing keys if not set or in dev environment.
@@ -95,9 +122,36 @@ export const env = createEnv({
NEXT_PUBLIC_TURNSTILE_SITE_KEY: v.optional(v.string(), "XXXX.DUMMY.TOKEN.XXXX"),
},
experimental__runtimeEnv: {
+ NEXT_PUBLIC_BASE_URL:
+ process.env.NEXT_PUBLIC_BASE_URL ||
+ // Vercel: https://vercel.com/docs/environment-variables/system-environment-variables
+ (process.env.VERCEL
+ ? process.env.VERCEL_ENV === "production" && process.env.VERCEL_PROJECT_PRODUCTION_URL
+ ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
+ : process.env.VERCEL_ENV === "preview" && process.env.VERCEL_BRANCH_URL
+ ? `https://${process.env.VERCEL_BRANCH_URL}`
+ : process.env.VERCEL_URL
+ ? `https://${process.env.VERCEL_URL}`
+ : undefined
+ : undefined) ||
+ // Netlify: https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables
+ (process.env.NETLIFY
+ ? process.env.CONTEXT === "production" && process.env.URL
+ ? `${process.env.URL}`
+ : process.env.DEPLOY_PRIME_URL
+ ? `${process.env.DEPLOY_PRIME_URL}`
+ : process.env.DEPLOY_URL
+ ? `${process.env.DEPLOY_URL}`
+ : undefined
+ : undefined) ||
+ // next dev
+ `http://localhost:${process.env.PORT || 3000}`,
NEXT_PUBLIC_GISCUS_CATEGORY_ID: process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID,
NEXT_PUBLIC_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_GISCUS_REPO_ID,
+ NEXT_PUBLIC_GITHUB_REPO: process.env.NEXT_PUBLIC_GITHUB_REPO,
NEXT_PUBLIC_ONION_DOMAIN: process.env.NEXT_PUBLIC_ONION_DOMAIN,
+ NEXT_PUBLIC_SITE_LOCALE: process.env.NEXT_PUBLIC_SITE_LOCALE,
+ NEXT_PUBLIC_SITE_TZ: process.env.NEXT_PUBLIC_SITE_TZ,
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
},
emptyStringAsUndefined: true,
diff --git a/lib/helpers/build-feed.ts b/lib/helpers/build-feed.ts
index 033273a7..12ddbb78 100644
--- a/lib/helpers/build-feed.ts
+++ b/lib/helpers/build-feed.ts
@@ -1,7 +1,7 @@
+import { env } from "../../lib/env";
import { Feed } from "feed";
import { getFrontMatter, getContent } from "./posts";
import * as config from "../config";
-import { BASE_URL, RELEASE_TIMESTAMP } from "../config/constants";
import type { Item as FeedItem } from "feed";
import ogImage from "../../app/opengraph-image.jpg";
@@ -12,20 +12,20 @@ import ogImage from "../../app/opengraph-image.jpg";
*/
export const buildFeed = async (): Promise
=> {
const feed = new Feed({
- id: `${BASE_URL}`,
- link: `${BASE_URL}`,
+ id: `${env.NEXT_PUBLIC_BASE_URL}`,
+ link: `${env.NEXT_PUBLIC_BASE_URL}`,
title: config.siteName,
description: config.description,
copyright: config.licenseUrl,
- updated: new Date(RELEASE_TIMESTAMP),
- image: `${BASE_URL}${ogImage.src}`,
+ updated: new Date(),
+ image: `${env.NEXT_PUBLIC_BASE_URL}${ogImage.src}`,
feedLinks: {
- rss: `${BASE_URL}/feed.xml`,
- atom: `${BASE_URL}/feed.atom`,
+ rss: `${env.NEXT_PUBLIC_BASE_URL}/feed.xml`,
+ atom: `${env.NEXT_PUBLIC_BASE_URL}/feed.atom`,
},
author: {
name: config.authorName,
- link: BASE_URL,
+ link: env.NEXT_PUBLIC_BASE_URL,
email: config.authorEmail,
},
});
@@ -41,7 +41,7 @@ export const buildFeed = async (): Promise => {
author: [
{
name: config.authorName,
- link: `${BASE_URL}`,
+ link: `${env.NEXT_PUBLIC_BASE_URL}`,
},
],
date: new Date(post.date),
diff --git a/lib/helpers/metadata.ts b/lib/helpers/metadata.ts
index 43033d2d..a0d71096 100644
--- a/lib/helpers/metadata.ts
+++ b/lib/helpers/metadata.ts
@@ -1,9 +1,9 @@
+import { env } from "../env";
import * as config from "../config";
-import { BASE_URL, SITE_LOCALE } from "../config/constants";
import type { Metadata } from "next";
export const defaultMetadata: Metadata = {
- metadataBase: new URL(BASE_URL),
+ metadataBase: env.NEXT_PUBLIC_BASE_URL ? new URL(env.NEXT_PUBLIC_BASE_URL) : undefined,
title: {
template: `%s – ${config.siteName}`,
default: `${config.siteName} – ${config.tagline}`,
@@ -16,7 +16,7 @@ export const defaultMetadata: Metadata = {
default: `${config.siteName} – ${config.tagline}`,
},
url: "/",
- locale: SITE_LOCALE?.replace("-", "_"),
+ locale: env.NEXT_PUBLIC_SITE_LOCALE.replace("-", "_"),
type: "website",
},
twitter: {
diff --git a/lib/helpers/posts.ts b/lib/helpers/posts.ts
index 7eccb521..c0906f20 100644
--- a/lib/helpers/posts.ts
+++ b/lib/helpers/posts.ts
@@ -1,10 +1,11 @@
+import { env } from "../../lib/env";
import path from "path";
import fs from "fs/promises";
import glob from "fast-glob";
import { unified } from "unified";
import { remarkHtml, remarkParse, remarkSmartypants, remarkFrontmatter } from "./remark-rehype-plugins";
import { decode } from "html-entities";
-import { BASE_URL, POSTS_DIR } from "../config/constants";
+import { POSTS_DIR } from "../config/constants";
export type FrontMatter = {
slug: string;
@@ -77,7 +78,7 @@ Promise => {
slug,
// validate/normalize the date string provided from front matter
date: new Date(frontmatter.date).toISOString(),
- permalink: `${BASE_URL}/${POSTS_DIR}/${slug}`,
+ permalink: `${env.NEXT_PUBLIC_BASE_URL}/${POSTS_DIR}/${slug}`,
} as FrontMatter;
} catch (error) {
console.error(`Failed to load front matter for post with slug "${slug}":`, error);
diff --git a/next.config.ts b/next.config.ts
index 073ffa47..35930665 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -12,21 +12,6 @@ import "./lib/env";
const nextConfig: NextConfig = {
reactStrictMode: true,
productionBrowserSourceMaps: true,
- env: {
- // same logic as metadataBase: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value
- NEXT_PUBLIC_BASE_URL:
- process.env.VERCEL_ENV === "production" && process.env.VERCEL_PROJECT_PRODUCTION_URL
- ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
- : process.env.VERCEL_ENV === "preview" && process.env.VERCEL_BRANCH_URL
- ? `https://${process.env.VERCEL_BRANCH_URL}`
- : process.env.VERCEL_URL
- ? `https://${process.env.VERCEL_URL}`
- : `http://localhost:${process.env.PORT || 3000}`,
-
- // freeze timestamp at build time for when server-side pages need a "last updated" date. calling Date.now() from
- // pages using getServerSideProps will return the current(ish) time instead, which is usually not what we want.
- NEXT_PUBLIC_RELEASE_TIMESTAMP: new Date().toISOString(),
- },
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
outputFileTracingIncludes: {
"/notes/[slug]/opengraph-image": [