mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 05:45:22 -04:00
clean up remaining NEXT_PUBLIC_ environment variables
This commit is contained in:
parent
f08aa16862
commit
b60fbcc15c
@ -10,7 +10,11 @@ RESEND_API_KEY=
|
|||||||
RESEND_FROM_EMAIL=
|
RESEND_FROM_EMAIL=
|
||||||
RESEND_TO_EMAIL=
|
RESEND_TO_EMAIL=
|
||||||
TURNSTILE_SECRET_KEY=
|
TURNSTILE_SECRET_KEY=
|
||||||
|
NEXT_PUBLIC_BASE_URL=
|
||||||
NEXT_PUBLIC_GISCUS_CATEGORY_ID=
|
NEXT_PUBLIC_GISCUS_CATEGORY_ID=
|
||||||
NEXT_PUBLIC_GISCUS_REPO_ID=
|
NEXT_PUBLIC_GISCUS_REPO_ID=
|
||||||
|
NEXT_PUBLIC_GITHUB_REPO=
|
||||||
NEXT_PUBLIC_ONION_DOMAIN=
|
NEXT_PUBLIC_ONION_DOMAIN=
|
||||||
|
NEXT_PUBLIC_SITE_LOCALE=
|
||||||
|
NEXT_PUBLIC_SITE_TZ=
|
||||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
|
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import { JsonLd } from "react-schemaorg";
|
import { JsonLd } from "react-schemaorg";
|
||||||
import PageTitle from "../../components/PageTitle";
|
import PageTitle from "../../components/PageTitle";
|
||||||
import Video from "../../components/Video";
|
import Video from "../../components/Video";
|
||||||
import { addMetadata } from "../../lib/helpers/metadata";
|
import { addMetadata } from "../../lib/helpers/metadata";
|
||||||
import { BASE_URL } from "../../lib/config/constants";
|
|
||||||
import type { VideoObject } from "schema-dts";
|
import type { VideoObject } from "schema-dts";
|
||||||
|
|
||||||
import mp4 from "./birthday.mp4";
|
import mp4 from "./birthday.mp4";
|
||||||
@ -26,9 +26,9 @@ const Page = () => {
|
|||||||
"@type": "VideoObject",
|
"@type": "VideoObject",
|
||||||
name: metadata.title as string,
|
name: metadata.title as string,
|
||||||
description: metadata.description as string,
|
description: metadata.description as string,
|
||||||
contentUrl: `${BASE_URL}${webm}`,
|
contentUrl: `${env.NEXT_PUBLIC_BASE_URL}${webm}`,
|
||||||
thumbnailUrl: `${BASE_URL}${thumbnail.src}`,
|
thumbnailUrl: `${env.NEXT_PUBLIC_BASE_URL}${thumbnail.src}`,
|
||||||
embedUrl: `${BASE_URL}/birthday`,
|
embedUrl: `${env.NEXT_PUBLIC_BASE_URL}/birthday`,
|
||||||
uploadDate: "1996-02-06T00:00:00Z",
|
uploadDate: "1996-02-06T00:00:00Z",
|
||||||
duration: "PT6M10S",
|
duration: "PT6M10S",
|
||||||
}}
|
}}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import { JsonLd } from "react-schemaorg";
|
import { JsonLd } from "react-schemaorg";
|
||||||
import PageTitle from "../../components/PageTitle";
|
import PageTitle from "../../components/PageTitle";
|
||||||
import Link from "../../components/Link";
|
import Link from "../../components/Link";
|
||||||
import Video from "../../components/Video";
|
import Video from "../../components/Video";
|
||||||
import { addMetadata } from "../../lib/helpers/metadata";
|
import { addMetadata } from "../../lib/helpers/metadata";
|
||||||
import { BASE_URL } from "../../lib/config/constants";
|
|
||||||
import type { VideoObject } from "schema-dts";
|
import type { VideoObject } from "schema-dts";
|
||||||
|
|
||||||
import webm from "./convention.webm";
|
import webm from "./convention.webm";
|
||||||
@ -28,9 +28,9 @@ const Page = () => {
|
|||||||
"@type": "VideoObject",
|
"@type": "VideoObject",
|
||||||
name: metadata.title as string,
|
name: metadata.title as string,
|
||||||
description: metadata.description as string,
|
description: metadata.description as string,
|
||||||
contentUrl: `${BASE_URL}${webm}`,
|
contentUrl: `${env.NEXT_PUBLIC_BASE_URL}${webm}`,
|
||||||
thumbnailUrl: `${BASE_URL}${thumbnail.src}`,
|
thumbnailUrl: `${env.NEXT_PUBLIC_BASE_URL}${thumbnail.src}`,
|
||||||
embedUrl: `${BASE_URL}/hillary`,
|
embedUrl: `${env.NEXT_PUBLIC_BASE_URL}/hillary`,
|
||||||
uploadDate: "2016-07-25T00:00:00Z",
|
uploadDate: "2016-07-25T00:00:00Z",
|
||||||
duration: "PT1M51S",
|
duration: "PT1M51S",
|
||||||
}}
|
}}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { env } from "../lib/env";
|
||||||
import { JsonLd } from "react-schemaorg";
|
import { JsonLd } from "react-schemaorg";
|
||||||
import { Analytics } from "@vercel/analytics/next";
|
import { Analytics } from "@vercel/analytics/next";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
@ -7,7 +8,7 @@ import Footer from "../components/Footer";
|
|||||||
import { SkipNavLink, SkipNavTarget } from "../components/SkipNav";
|
import { SkipNavLink, SkipNavTarget } from "../components/SkipNav";
|
||||||
import { defaultMetadata } from "../lib/helpers/metadata";
|
import { defaultMetadata } from "../lib/helpers/metadata";
|
||||||
import * as config from "../lib/config";
|
import * as config from "../lib/config";
|
||||||
import { BASE_URL, MAX_WIDTH, SITE_LOCALE } from "../lib/config/constants";
|
import { MAX_WIDTH } from "../lib/config/constants";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import type { Person, WebSite } from "schema-dts";
|
import type { Person, WebSite } from "schema-dts";
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ export const metadata: Metadata = defaultMetadata;
|
|||||||
|
|
||||||
const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||||
return (
|
return (
|
||||||
<html lang={SITE_LOCALE || "en-US"} suppressHydrationWarning>
|
<html lang={env.NEXT_PUBLIC_SITE_LOCALE} suppressHydrationWarning>
|
||||||
<head>
|
<head>
|
||||||
<ThemeScript />
|
<ThemeScript />
|
||||||
|
|
||||||
@ -29,12 +30,12 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
|||||||
item={{
|
item={{
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"@id": `${BASE_URL}/#person`,
|
"@id": `${env.NEXT_PUBLIC_BASE_URL}/#person`,
|
||||||
name: config.authorName,
|
name: config.authorName,
|
||||||
url: BASE_URL,
|
url: env.NEXT_PUBLIC_BASE_URL,
|
||||||
image: [`${BASE_URL}/opengraph-image.jpg`],
|
image: [`${env.NEXT_PUBLIC_BASE_URL}/opengraph-image.jpg`],
|
||||||
sameAs: [
|
sameAs: [
|
||||||
`${BASE_URL}`,
|
env.NEXT_PUBLIC_BASE_URL!,
|
||||||
`https://${config.authorSocial?.mastodon}`,
|
`https://${config.authorSocial?.mastodon}`,
|
||||||
`https://github.com/${config.authorSocial?.github}`,
|
`https://github.com/${config.authorSocial?.github}`,
|
||||||
`https://bsky.app/profile/${config.authorSocial?.bluesky}`,
|
`https://bsky.app/profile/${config.authorSocial?.bluesky}`,
|
||||||
@ -51,12 +52,12 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
|||||||
item={{
|
item={{
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "WebSite",
|
"@type": "WebSite",
|
||||||
"@id": `${BASE_URL}/#website`,
|
"@id": `${env.NEXT_PUBLIC_BASE_URL}/#website`,
|
||||||
name: config.siteName,
|
name: config.siteName,
|
||||||
url: BASE_URL,
|
url: env.NEXT_PUBLIC_BASE_URL,
|
||||||
author: config.authorName,
|
author: config.authorName,
|
||||||
description: config.description,
|
description: config.description,
|
||||||
inLanguage: SITE_LOCALE,
|
inLanguage: env.NEXT_PUBLIC_SITE_LOCALE,
|
||||||
license: config.licenseUrl,
|
license: config.licenseUrl,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import { JsonLd } from "react-schemaorg";
|
import { JsonLd } from "react-schemaorg";
|
||||||
import PageTitle from "../../components/PageTitle";
|
import PageTitle from "../../components/PageTitle";
|
||||||
import Link from "../../components/Link";
|
import Link from "../../components/Link";
|
||||||
import Video from "../../components/Video";
|
import Video from "../../components/Video";
|
||||||
import { addMetadata } from "../../lib/helpers/metadata";
|
import { addMetadata } from "../../lib/helpers/metadata";
|
||||||
import { BASE_URL } from "../../lib/config/constants";
|
|
||||||
import type { VideoObject } from "schema-dts";
|
import type { VideoObject } from "schema-dts";
|
||||||
|
|
||||||
import mp4 from "./leo.mp4";
|
import mp4 from "./leo.mp4";
|
||||||
@ -28,9 +28,9 @@ const Page = () => {
|
|||||||
"@type": "VideoObject",
|
"@type": "VideoObject",
|
||||||
name: metadata.title as string,
|
name: metadata.title as string,
|
||||||
description: metadata.description as string,
|
description: metadata.description as string,
|
||||||
contentUrl: `${BASE_URL}${webm}`,
|
contentUrl: `${env.NEXT_PUBLIC_BASE_URL}${webm}`,
|
||||||
thumbnailUrl: `${BASE_URL}${thumbnail.src}`,
|
thumbnailUrl: `${env.NEXT_PUBLIC_BASE_URL}${thumbnail.src}`,
|
||||||
embedUrl: `${BASE_URL}/leo`,
|
embedUrl: `${env.NEXT_PUBLIC_BASE_URL}/leo`,
|
||||||
uploadDate: "2007-05-10T00:00:00Z",
|
uploadDate: "2007-05-10T00:00:00Z",
|
||||||
duration: "PT1M48S",
|
duration: "PT1M48S",
|
||||||
}}
|
}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { env } from "../lib/env";
|
||||||
import * as config from "../lib/config";
|
import * as config from "../lib/config";
|
||||||
import { SITE_LOCALE } from "../lib/config/constants";
|
|
||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
const manifest = (): MetadataRoute.Manifest => {
|
const manifest = (): MetadataRoute.Manifest => {
|
||||||
@ -8,7 +8,7 @@ const manifest = (): MetadataRoute.Manifest => {
|
|||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
short_name: config.siteName,
|
short_name: config.siteName,
|
||||||
description: config.description,
|
description: config.description,
|
||||||
lang: SITE_LOCALE,
|
lang: env.NEXT_PUBLIC_SITE_LOCALE,
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: "/icon.png",
|
src: "/icon.png",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { env } from "../../../lib/env";
|
||||||
import { connection } from "next/server";
|
import { connection } from "next/server";
|
||||||
import CountUp from "../../../components/CountUp";
|
import CountUp from "../../../components/CountUp";
|
||||||
import redis from "../../../lib/redis";
|
import redis from "../../../lib/redis";
|
||||||
import { SITE_LOCALE } from "../../../lib/config/constants";
|
|
||||||
|
|
||||||
const HitCounter = async ({ slug }: { slug: string }) => {
|
const HitCounter = async ({ slug }: { slug: string }) => {
|
||||||
await connection();
|
await connection();
|
||||||
@ -16,7 +16,7 @@ const HitCounter = async ({ slug }: { slug: string }) => {
|
|||||||
|
|
||||||
// we have data!
|
// we have data!
|
||||||
return (
|
return (
|
||||||
<span title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").format(hits)} ${hits === 1 ? "view" : "views"}`}>
|
<span title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(hits)} ${hits === 1 ? "view" : "views"}`}>
|
||||||
<CountUp start={0} end={hits} delay={0} duration={1.5} />
|
<CountUp start={0} end={hits} delay={0} duration={1.5} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,7 @@ import HitCounter from "./counter";
|
|||||||
import { getSlugs, getFrontMatter } from "../../../lib/helpers/posts";
|
import { getSlugs, getFrontMatter } from "../../../lib/helpers/posts";
|
||||||
import { addMetadata } from "../../../lib/helpers/metadata";
|
import { addMetadata } from "../../../lib/helpers/metadata";
|
||||||
import * as config from "../../../lib/config";
|
import * as config from "../../../lib/config";
|
||||||
import { BASE_URL, POSTS_DIR, SITE_LOCALE } from "../../../lib/config/constants";
|
import { POSTS_DIR } from "../../../lib/config/constants";
|
||||||
import { size as ogImageSize } from "./opengraph-image";
|
import { size as ogImageSize } from "./opengraph-image";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import type { BlogPosting } from "schema-dts";
|
import type { BlogPosting } from "schema-dts";
|
||||||
@ -72,18 +72,18 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
|||||||
url: frontmatter!.permalink,
|
url: frontmatter!.permalink,
|
||||||
image: {
|
image: {
|
||||||
"@type": "ImageObject",
|
"@type": "ImageObject",
|
||||||
contentUrl: `${BASE_URL}/${POSTS_DIR}/${frontmatter!.slug}/opengraph-image`,
|
contentUrl: `${env.NEXT_PUBLIC_BASE_URL}/${POSTS_DIR}/${frontmatter!.slug}/opengraph-image`,
|
||||||
width: `${ogImageSize.width}`,
|
width: `${ogImageSize.width}`,
|
||||||
height: `${ogImageSize.height}`,
|
height: `${ogImageSize.height}`,
|
||||||
},
|
},
|
||||||
keywords: frontmatter!.tags?.join(", "),
|
keywords: frontmatter!.tags?.join(", "),
|
||||||
datePublished: frontmatter!.date,
|
datePublished: frontmatter!.date,
|
||||||
dateModified: frontmatter!.date,
|
dateModified: frontmatter!.date,
|
||||||
inLanguage: SITE_LOCALE,
|
inLanguage: env.NEXT_PUBLIC_SITE_LOCALE,
|
||||||
license: config.licenseUrl,
|
license: config.licenseUrl,
|
||||||
author: {
|
author: {
|
||||||
// defined in app/layout.tsx
|
// defined in app/layout.tsx
|
||||||
"@id": `${BASE_URL}/#person`,
|
"@id": `${env.NEXT_PUBLIC_BASE_URL}/#person`,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -111,7 +111,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
|||||||
|
|
||||||
<div className={styles.metaItem}>
|
<div className={styles.metaItem}>
|
||||||
<Link
|
<Link
|
||||||
href={`https://github.com/${config.githubRepo}/blob/main/${POSTS_DIR}/${frontmatter!.slug}/index.mdx`}
|
href={`https://github.com/${env.NEXT_PUBLIC_GITHUB_REPO}/blob/main/${POSTS_DIR}/${frontmatter!.slug}/index.mdx`}
|
||||||
title={`Edit "${frontmatter!.title}" on GitHub`}
|
title={`Edit "${frontmatter!.title}" on GitHub`}
|
||||||
plain
|
plain
|
||||||
className={styles.metaLink}
|
className={styles.metaLink}
|
||||||
@ -121,26 +121,23 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* only count hits on production site */}
|
<div
|
||||||
{env.VERCEL_ENV === "production" ? (
|
className={styles.metaItem}
|
||||||
<div
|
style={{
|
||||||
className={styles.metaItem}
|
// fix potential layout shift when number of hits loads
|
||||||
style={{
|
minWidth: "7em",
|
||||||
// fix potential layout shift when number of hits loads
|
marginRight: 0,
|
||||||
minWidth: "7em",
|
}}
|
||||||
marginRight: 0,
|
>
|
||||||
}}
|
<EyeIcon size="1.2em" className={styles.metaIcon} />
|
||||||
|
<Suspense
|
||||||
|
// when this loads, the component will count up from zero to the actual number of hits, so we can simply
|
||||||
|
// show a zero here as a "loading indicator"
|
||||||
|
fallback={<span>0</span>}
|
||||||
>
|
>
|
||||||
<EyeIcon size="1.2em" className={styles.metaIcon} />
|
<HitCounter slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
||||||
<Suspense
|
</Suspense>
|
||||||
// when this loads, the component will count up from zero to the actual number of hits, so we can simply
|
</div>
|
||||||
// show a zero here as a "loading indicator"
|
|
||||||
fallback={<span>0</span>}
|
|
||||||
>
|
|
||||||
<HitCounter slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
|
@ -7,7 +7,6 @@ import Link from "../../components/Link";
|
|||||||
import RelativeTime from "../../components/RelativeTime";
|
import RelativeTime from "../../components/RelativeTime";
|
||||||
import { addMetadata } from "../../lib/helpers/metadata";
|
import { addMetadata } from "../../lib/helpers/metadata";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
import { SITE_LOCALE } from "../../lib/config/constants";
|
|
||||||
import type { User } from "@octokit/graphql-schema";
|
import type { User } from "@octokit/graphql-schema";
|
||||||
|
|
||||||
import styles from "./page.module.css";
|
import styles from "./page.module.css";
|
||||||
@ -121,12 +120,12 @@ const Page = async () => {
|
|||||||
<div className={styles.metaItem}>
|
<div className={styles.metaItem}>
|
||||||
<Link
|
<Link
|
||||||
href={`${repo!.url}/stargazers`}
|
href={`${repo!.url}/stargazers`}
|
||||||
title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.stargazerCount)} ${repo!.stargazerCount === 1 ? "star" : "stars"}`}
|
title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.stargazerCount)} ${repo!.stargazerCount === 1 ? "star" : "stars"}`}
|
||||||
plain
|
plain
|
||||||
className={styles.metaLink}
|
className={styles.metaLink}
|
||||||
>
|
>
|
||||||
<StarIcon size="1.25em" className={styles.metaIcon} />
|
<StarIcon size="1.25em" className={styles.metaIcon} />
|
||||||
<span>{Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.stargazerCount)}</span>
|
<span>{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.stargazerCount)}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -135,12 +134,12 @@ const Page = async () => {
|
|||||||
<div className={styles.metaItem}>
|
<div className={styles.metaItem}>
|
||||||
<Link
|
<Link
|
||||||
href={`${repo!.url}/network/members`}
|
href={`${repo!.url}/network/members`}
|
||||||
title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.forkCount)} ${repo!.forkCount === 1 ? "fork" : "forks"}`}
|
title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.forkCount)} ${repo!.forkCount === 1 ? "fork" : "forks"}`}
|
||||||
plain
|
plain
|
||||||
className={styles.metaLink}
|
className={styles.metaLink}
|
||||||
>
|
>
|
||||||
<GitForkIcon size="1.25em" className={styles.metaIcon} />
|
<GitForkIcon size="1.25em" className={styles.metaIcon} />
|
||||||
<span>{Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.forkCount)}</span>
|
<span>{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.forkCount)}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BASE_URL } from "../lib/config/constants";
|
import { env } from "../lib/env";
|
||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
const robots = (): MetadataRoute.Robots => ({
|
const robots = (): MetadataRoute.Robots => ({
|
||||||
@ -8,7 +8,7 @@ const robots = (): MetadataRoute.Robots => ({
|
|||||||
disallow: ["/api/", "/404", "/500"],
|
disallow: ["/api/", "/404", "/500"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sitemap: `${BASE_URL}/sitemap.xml`,
|
sitemap: `${env.NEXT_PUBLIC_BASE_URL}/sitemap.xml`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default robots;
|
export default robots;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { env } from "../lib/env";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import glob from "fast-glob";
|
import glob from "fast-glob";
|
||||||
import { getFrontMatter } from "../lib/helpers/posts";
|
import { getFrontMatter } from "../lib/helpers/posts";
|
||||||
import { BASE_URL, RELEASE_TIMESTAMP } from "../lib/config/constants";
|
|
||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from "next";
|
||||||
|
|
||||||
const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
|
const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
|
||||||
@ -9,9 +9,9 @@ const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
|
|||||||
const routes: MetadataRoute.Sitemap = [
|
const routes: MetadataRoute.Sitemap = [
|
||||||
{
|
{
|
||||||
// homepage
|
// homepage
|
||||||
url: `${BASE_URL}`,
|
url: `${env.NEXT_PUBLIC_BASE_URL}`,
|
||||||
priority: 1.0,
|
priority: 1.0,
|
||||||
lastModified: new Date(RELEASE_TIMESTAMP), // timestamp frozen when a new build is deployed
|
lastModified: new Date(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
|
|||||||
).forEach((route) => {
|
).forEach((route) => {
|
||||||
routes.push({
|
routes.push({
|
||||||
// remove matching page.(tsx|mdx) file and make all URLs absolute
|
// remove matching page.(tsx|mdx) file and make all URLs absolute
|
||||||
url: `${BASE_URL}/${route.replace(/\/page\.(tsx|mdx)$/, "")}`,
|
url: `${env.NEXT_PUBLIC_BASE_URL}/${route.replace(/\/page\.(tsx|mdx)$/, "")}`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { env } from "../../lib/env";
|
import { env } from "../../lib/env";
|
||||||
import Giscus from "@giscus/react";
|
import Giscus from "@giscus/react";
|
||||||
import * as config from "../../lib/config";
|
|
||||||
import type { GiscusProps } from "@giscus/react";
|
import type { GiscusProps } from "@giscus/react";
|
||||||
|
|
||||||
export type CommentsProps = {
|
export type CommentsProps = {
|
||||||
@ -21,7 +20,7 @@ const Comments = ({ title }: CommentsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Giscus
|
<Giscus
|
||||||
repo={config.githubRepo as GiscusProps["repo"]}
|
repo={env.NEXT_PUBLIC_GITHUB_REPO as GiscusProps["repo"]}
|
||||||
repoId={env.NEXT_PUBLIC_GISCUS_REPO_ID}
|
repoId={env.NEXT_PUBLIC_GISCUS_REPO_ID}
|
||||||
term={title}
|
term={title}
|
||||||
category="Comments"
|
category="Comments"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { HeartIcon } from "lucide-react";
|
import { HeartIcon } from "lucide-react";
|
||||||
import Link from "../Link";
|
import Link from "../Link";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
import { RELEASE_TIMESTAMP } from "../../lib/config/constants";
|
|
||||||
import type { ComponentPropsWithoutRef } from "react";
|
import type { ComponentPropsWithoutRef } from "react";
|
||||||
|
|
||||||
import styles from "./Footer.module.css";
|
import styles from "./Footer.module.css";
|
||||||
@ -22,7 +22,7 @@ const Footer = ({ className, ...rest }: FooterProps) => {
|
|||||||
<Link href="/previously" title="Previously on..." plain className={styles.link}>
|
<Link href="/previously" title="Previously on..." plain className={styles.link}>
|
||||||
{config.copyrightYearStart}
|
{config.copyrightYearStart}
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
– {new Date(RELEASE_TIMESTAMP).getUTCFullYear()}.
|
– {new Date().getUTCFullYear()}.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -53,7 +53,7 @@ const Footer = ({ className, ...rest }: FooterProps) => {
|
|||||||
</Link>
|
</Link>
|
||||||
.{" "}
|
.{" "}
|
||||||
<Link
|
<Link
|
||||||
href={`https://github.com/${config.githubRepo}`}
|
href={`https://github.com/${env.NEXT_PUBLIC_GITHUB_REPO}`}
|
||||||
title="View Source on GitHub"
|
title="View Source on GitHub"
|
||||||
plain
|
plain
|
||||||
className={clsx(styles.link, styles.underline)}
|
className={clsx(styles.link, styles.underline)}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import { format, formatISO } from "date-fns";
|
import { format, formatISO } from "date-fns";
|
||||||
import { enUS } from "date-fns/locale";
|
import { enUS } from "date-fns/locale";
|
||||||
import { tz } from "@date-fns/tz";
|
import { tz } from "@date-fns/tz";
|
||||||
import { utc } from "@date-fns/utc";
|
import { utc } from "@date-fns/utc";
|
||||||
import { SITE_TZ } from "../../lib/config/constants";
|
|
||||||
import type { ComponentPropsWithoutRef } from "react";
|
import type { ComponentPropsWithoutRef } from "react";
|
||||||
|
|
||||||
export type TimeProps = ComponentPropsWithoutRef<"time"> & {
|
export type TimeProps = ComponentPropsWithoutRef<"time"> & {
|
||||||
@ -14,10 +14,10 @@ const Time = ({ date, format: formatStr = "PPpp", ...rest }: TimeProps) => {
|
|||||||
return (
|
return (
|
||||||
<time
|
<time
|
||||||
dateTime={formatISO(date, { in: utc })}
|
dateTime={formatISO(date, { in: utc })}
|
||||||
title={format(date, "MMM d, y, h:mm a O", { in: tz(SITE_TZ), locale: enUS })}
|
title={format(date, "MMM d, y, h:mm a O", { in: tz(env.NEXT_PUBLIC_SITE_TZ), locale: enUS })}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{format(date, formatStr, { in: tz(SITE_TZ), locale: enUS })}
|
{format(date, formatStr, { in: tz(env.NEXT_PUBLIC_SITE_TZ), locale: enUS })}
|
||||||
</time>
|
</time>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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. */
|
/** Path to directory with .mdx files, relative to project root. */
|
||||||
export const POSTS_DIR = "notes";
|
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. */
|
/** Maximum width of content wrapper (e.g. for images) in pixels. */
|
||||||
export const MAX_WIDTH = 865;
|
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!;
|
|
||||||
|
@ -7,7 +7,6 @@ export const license = "Creative Commons Attribution 4.0 International";
|
|||||||
export const licenseAbbr = "CC-BY-4.0";
|
export const licenseAbbr = "CC-BY-4.0";
|
||||||
export const licenseUrl = "https://creativecommons.org/licenses/by/4.0/";
|
export const licenseUrl = "https://creativecommons.org/licenses/by/4.0/";
|
||||||
export const copyrightYearStart = 2001;
|
export const copyrightYearStart = 2001;
|
||||||
export const githubRepo = "jakejarvis/jarv.is";
|
|
||||||
|
|
||||||
// Me info
|
// Me info
|
||||||
export const authorName = "Jake Jarvis";
|
export const authorName = "Jake Jarvis";
|
||||||
|
54
lib/env.ts
54
lib/env.ts
@ -64,6 +64,13 @@ export const env = createEnv({
|
|||||||
TURNSTILE_SECRET_KEY: v.optional(v.string(), "1x0000000000000000000000000000000AA"),
|
TURNSTILE_SECRET_KEY: v.optional(v.string(), "1x0000000000000000000000000000000AA"),
|
||||||
},
|
},
|
||||||
client: {
|
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.
|
* 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()),
|
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
|
* 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
|
* 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"))),
|
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
|
* 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.
|
* 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"),
|
NEXT_PUBLIC_TURNSTILE_SITE_KEY: v.optional(v.string(), "XXXX.DUMMY.TOKEN.XXXX"),
|
||||||
},
|
},
|
||||||
experimental__runtimeEnv: {
|
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_CATEGORY_ID: process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID,
|
||||||
NEXT_PUBLIC_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_GISCUS_REPO_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_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,
|
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
|
||||||
},
|
},
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import { Feed } from "feed";
|
import { Feed } from "feed";
|
||||||
import { getFrontMatter, getContent } from "./posts";
|
import { getFrontMatter, getContent } from "./posts";
|
||||||
import * as config from "../config";
|
import * as config from "../config";
|
||||||
import { BASE_URL, RELEASE_TIMESTAMP } from "../config/constants";
|
|
||||||
import type { Item as FeedItem } from "feed";
|
import type { Item as FeedItem } from "feed";
|
||||||
|
|
||||||
import ogImage from "../../app/opengraph-image.jpg";
|
import ogImage from "../../app/opengraph-image.jpg";
|
||||||
@ -12,20 +12,20 @@ import ogImage from "../../app/opengraph-image.jpg";
|
|||||||
*/
|
*/
|
||||||
export const buildFeed = async (): Promise<Feed> => {
|
export const buildFeed = async (): Promise<Feed> => {
|
||||||
const feed = new Feed({
|
const feed = new Feed({
|
||||||
id: `${BASE_URL}`,
|
id: `${env.NEXT_PUBLIC_BASE_URL}`,
|
||||||
link: `${BASE_URL}`,
|
link: `${env.NEXT_PUBLIC_BASE_URL}`,
|
||||||
title: config.siteName,
|
title: config.siteName,
|
||||||
description: config.description,
|
description: config.description,
|
||||||
copyright: config.licenseUrl,
|
copyright: config.licenseUrl,
|
||||||
updated: new Date(RELEASE_TIMESTAMP),
|
updated: new Date(),
|
||||||
image: `${BASE_URL}${ogImage.src}`,
|
image: `${env.NEXT_PUBLIC_BASE_URL}${ogImage.src}`,
|
||||||
feedLinks: {
|
feedLinks: {
|
||||||
rss: `${BASE_URL}/feed.xml`,
|
rss: `${env.NEXT_PUBLIC_BASE_URL}/feed.xml`,
|
||||||
atom: `${BASE_URL}/feed.atom`,
|
atom: `${env.NEXT_PUBLIC_BASE_URL}/feed.atom`,
|
||||||
},
|
},
|
||||||
author: {
|
author: {
|
||||||
name: config.authorName,
|
name: config.authorName,
|
||||||
link: BASE_URL,
|
link: env.NEXT_PUBLIC_BASE_URL,
|
||||||
email: config.authorEmail,
|
email: config.authorEmail,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -41,7 +41,7 @@ export const buildFeed = async (): Promise<Feed> => {
|
|||||||
author: [
|
author: [
|
||||||
{
|
{
|
||||||
name: config.authorName,
|
name: config.authorName,
|
||||||
link: `${BASE_URL}`,
|
link: `${env.NEXT_PUBLIC_BASE_URL}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
date: new Date(post.date),
|
date: new Date(post.date),
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import { env } from "../env";
|
||||||
import * as config from "../config";
|
import * as config from "../config";
|
||||||
import { BASE_URL, SITE_LOCALE } from "../config/constants";
|
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const defaultMetadata: Metadata = {
|
export const defaultMetadata: Metadata = {
|
||||||
metadataBase: new URL(BASE_URL),
|
metadataBase: env.NEXT_PUBLIC_BASE_URL ? new URL(env.NEXT_PUBLIC_BASE_URL) : undefined,
|
||||||
title: {
|
title: {
|
||||||
template: `%s – ${config.siteName}`,
|
template: `%s – ${config.siteName}`,
|
||||||
default: `${config.siteName} – ${config.tagline}`,
|
default: `${config.siteName} – ${config.tagline}`,
|
||||||
@ -16,7 +16,7 @@ export const defaultMetadata: Metadata = {
|
|||||||
default: `${config.siteName} – ${config.tagline}`,
|
default: `${config.siteName} – ${config.tagline}`,
|
||||||
},
|
},
|
||||||
url: "/",
|
url: "/",
|
||||||
locale: SITE_LOCALE?.replace("-", "_"),
|
locale: env.NEXT_PUBLIC_SITE_LOCALE.replace("-", "_"),
|
||||||
type: "website",
|
type: "website",
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import { env } from "../../lib/env";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import glob from "fast-glob";
|
import glob from "fast-glob";
|
||||||
import { unified } from "unified";
|
import { unified } from "unified";
|
||||||
import { remarkHtml, remarkParse, remarkSmartypants, remarkFrontmatter } from "./remark-rehype-plugins";
|
import { remarkHtml, remarkParse, remarkSmartypants, remarkFrontmatter } from "./remark-rehype-plugins";
|
||||||
import { decode } from "html-entities";
|
import { decode } from "html-entities";
|
||||||
import { BASE_URL, POSTS_DIR } from "../config/constants";
|
import { POSTS_DIR } from "../config/constants";
|
||||||
|
|
||||||
export type FrontMatter = {
|
export type FrontMatter = {
|
||||||
slug: string;
|
slug: string;
|
||||||
@ -77,7 +78,7 @@ Promise<any> => {
|
|||||||
slug,
|
slug,
|
||||||
// validate/normalize the date string provided from front matter
|
// validate/normalize the date string provided from front matter
|
||||||
date: new Date(frontmatter.date).toISOString(),
|
date: new Date(frontmatter.date).toISOString(),
|
||||||
permalink: `${BASE_URL}/${POSTS_DIR}/${slug}`,
|
permalink: `${env.NEXT_PUBLIC_BASE_URL}/${POSTS_DIR}/${slug}`,
|
||||||
} as FrontMatter;
|
} as FrontMatter;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to load front matter for post with slug "${slug}":`, error);
|
console.error(`Failed to load front matter for post with slug "${slug}":`, error);
|
||||||
|
@ -12,21 +12,6 @@ import "./lib/env";
|
|||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
productionBrowserSourceMaps: 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"],
|
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
|
||||||
outputFileTracingIncludes: {
|
outputFileTracingIncludes: {
|
||||||
"/notes/[slug]/opengraph-image": [
|
"/notes/[slug]/opengraph-image": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user