mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 04:25:22 -04:00
refactor constants
This commit is contained in:
parent
0ade75716e
commit
37fa6101f6
@ -7,7 +7,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 } from "../lib/config/constants";
|
import { BASE_URL, MAX_WIDTH, SITE_LOCALE } 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 +21,7 @@ export const metadata: Metadata = defaultMetadata;
|
|||||||
|
|
||||||
const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||||
return (
|
return (
|
||||||
<html lang={config.siteLocale} suppressHydrationWarning>
|
<html lang={SITE_LOCALE || "en-US"} suppressHydrationWarning>
|
||||||
<head>
|
<head>
|
||||||
<ThemeScript />
|
<ThemeScript />
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
|||||||
name: config.siteName,
|
name: config.siteName,
|
||||||
url: BASE_URL,
|
url: BASE_URL,
|
||||||
author: config.authorName,
|
author: config.authorName,
|
||||||
description: config.longDescription,
|
description: config.description,
|
||||||
inLanguage: config.siteLocale,
|
inLanguage: SITE_LOCALE,
|
||||||
license: config.licenseUrl,
|
license: config.licenseUrl,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
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 => {
|
||||||
@ -6,8 +7,8 @@ const manifest = (): MetadataRoute.Manifest => {
|
|||||||
name: config.siteName,
|
name: config.siteName,
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
short_name: config.siteName,
|
short_name: config.siteName,
|
||||||
description: config.longDescription,
|
description: config.description,
|
||||||
lang: config.siteLocale,
|
lang: SITE_LOCALE,
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: "/icon.png",
|
src: "/icon.png",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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 { siteLocale } from "../../../lib/config";
|
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(siteLocale || "en-US").format(hits)} ${hits === 1 ? "view" : "views"}`}>
|
<span title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").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 } from "../../../lib/config/constants";
|
import { BASE_URL, POSTS_DIR, SITE_LOCALE } 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";
|
||||||
@ -79,7 +79,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
|||||||
keywords: frontmatter!.tags?.join(", "),
|
keywords: frontmatter!.tags?.join(", "),
|
||||||
datePublished: frontmatter!.date,
|
datePublished: frontmatter!.date,
|
||||||
dateModified: frontmatter!.date,
|
dateModified: frontmatter!.date,
|
||||||
inLanguage: config.siteLocale,
|
inLanguage: SITE_LOCALE,
|
||||||
license: config.licenseUrl,
|
license: config.licenseUrl,
|
||||||
author: {
|
author: {
|
||||||
// defined in app/layout.tsx
|
// defined in app/layout.tsx
|
||||||
|
@ -7,6 +7,7 @@ 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";
|
||||||
@ -120,12 +121,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(config.siteLocale || "en-US").format(repo!.stargazerCount)} ${repo!.stargazerCount === 1 ? "star" : "stars"}`}
|
title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").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(config.siteLocale || "en-US").format(repo!.stargazerCount)}</span>
|
<span>{Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.stargazerCount)}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -134,12 +135,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(config.siteLocale || "en-US").format(repo!.forkCount)} ${repo!.forkCount === 1 ? "fork" : "forks"}`}
|
title={`${Intl.NumberFormat(SITE_LOCALE || "en-US").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(config.siteLocale || "en-US").format(repo!.forkCount)}</span>
|
<span>{Intl.NumberFormat(SITE_LOCALE || "en-US").format(repo!.forkCount)}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -2,7 +2,7 @@ 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 * as config from "../../lib/config";
|
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(config.timeZone), locale: enUS })}
|
title={format(date, "MMM d, y, h:mm a O", { in: tz(SITE_TZ), locale: enUS })}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{format(date, formatStr, { in: tz(config.timeZone), locale: enUS })}
|
{format(date, formatStr, { in: tz(SITE_TZ), locale: enUS })}
|
||||||
</time>
|
</time>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,30 @@
|
|||||||
// path to directory with .mdx files, relative to project root
|
/**
|
||||||
|
* 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";
|
export const POSTS_DIR = "notes";
|
||||||
|
|
||||||
// path to an image used in various places to represent the site, relative to project root
|
/**
|
||||||
// IMPORTANT: must be included in next.config.ts under "outputFileTracingIncludes"
|
* Path to an image used in various places to represent the site, relative to project root. This path must be included
|
||||||
|
* in [next.config.ts](../../next.config.ts) under `outputFileTracingIncludes`.
|
||||||
|
*/
|
||||||
export const AVATAR_PATH = "app/avatar.jpg";
|
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;
|
||||||
|
|
||||||
// defined in next.config.ts
|
/** 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!;
|
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!;
|
export const RELEASE_TIMESTAMP = process.env.NEXT_PUBLIC_RELEASE_TIMESTAMP!;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
// Site info
|
// Site info
|
||||||
export const siteName = "Jake Jarvis";
|
export const siteName = "Jake Jarvis";
|
||||||
export const siteLocale = "en-US";
|
export const tagline = "Frontend Web Developer in Boston, MA";
|
||||||
export const timeZone = "America/New_York"; // https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
export const description =
|
||||||
export const shortDescription = "Frontend Web Developer in Boston, MA";
|
|
||||||
export const longDescription =
|
|
||||||
"Hi there! I'm a frontend web developer based in Boston, Massachusetts specializing in TypeScript, React, Next.js, and other JavaScript frameworks.";
|
"Hi there! I'm a frontend web developer based in Boston, Massachusetts specializing in TypeScript, React, Next.js, and other JavaScript frameworks.";
|
||||||
export const license = "Creative Commons Attribution 4.0 International";
|
export const license = "Creative Commons Attribution 4.0 International";
|
||||||
export const licenseAbbr = "CC-BY-4.0";
|
export const licenseAbbr = "CC-BY-4.0";
|
||||||
|
10
lib/env.ts
10
lib/env.ts
@ -11,15 +11,15 @@ export const env = createEnv({
|
|||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
/**
|
/**
|
||||||
* Required. GitHub API token used for /projects grid. Only needs the `public_repo` scope since we don't need/want
|
* Required. GitHub API token used for [/projects](../app/projects/page.tsx) grid. Only needs the `public_repo`
|
||||||
* to change anything, obviously.
|
* scope since we don't need/want to change anything, obviously.
|
||||||
*
|
*
|
||||||
* @see https://github.com/settings/tokens/new?scopes=public_repo
|
* @see https://github.com/settings/tokens/new?scopes=public_repo
|
||||||
*/
|
*/
|
||||||
GITHUB_TOKEN: v.optional(v.pipe(v.string(), v.startsWith("ghp_"))),
|
GITHUB_TOKEN: v.optional(v.pipe(v.string(), v.startsWith("ghp_"))),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required. Redis storage credentials for hit counter's server component (app/notes/[slug]/counter.tsx) and API
|
* Required. Redis storage credentials for hit counter's [server component](../app/notes/[slug]/counter.tsx) and API
|
||||||
* endpoint. Currently set automatically by Vercel's Upstash integration.
|
* endpoint. Currently set automatically by Vercel's Upstash integration.
|
||||||
*
|
*
|
||||||
* @see https://upstash.com/docs/redis/sdks/ts/getstarted
|
* @see https://upstash.com/docs/redis/sdks/ts/getstarted
|
||||||
@ -27,7 +27,7 @@ export const env = createEnv({
|
|||||||
*/
|
*/
|
||||||
KV_REST_API_TOKEN: v.string(),
|
KV_REST_API_TOKEN: v.string(),
|
||||||
/**
|
/**
|
||||||
* Required. Redis storage credentials for hit counter's server component (app/notes/[slug]/counter.tsx) and API
|
* Required. Redis storage credentials for hit counter's [server component](../app/notes/[slug]/counter.tsx) and API
|
||||||
* endpoint. Currently set automatically by Vercel's Upstash integration.
|
* endpoint. Currently set automatically by Vercel's Upstash integration.
|
||||||
*
|
*
|
||||||
* @see https://upstash.com/docs/redis/sdks/ts/getstarted
|
* @see https://upstash.com/docs/redis/sdks/ts/getstarted
|
||||||
@ -36,7 +36,7 @@ export const env = createEnv({
|
|||||||
KV_REST_API_URL: v.pipe(v.string(), v.url(), v.startsWith("https://"), v.endsWith(".upstash.io")),
|
KV_REST_API_URL: v.pipe(v.string(), v.url(), v.startsWith("https://"), v.endsWith(".upstash.io")),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required. Uses Resend API to send contact form submissions via a server action (see app/contact/actions.ts). May
|
* Required. Uses Resend API to send contact form submissions via a [server action](../app/contact/action.ts). May
|
||||||
* be set automatically by Vercel's Resend integration.
|
* be set automatically by Vercel's Resend integration.
|
||||||
*
|
*
|
||||||
* @see https://resend.com/api-keys
|
* @see https://resend.com/api-keys
|
||||||
|
@ -15,7 +15,7 @@ export const buildFeed = async (): Promise<Feed> => {
|
|||||||
id: `${BASE_URL}`,
|
id: `${BASE_URL}`,
|
||||||
link: `${BASE_URL}`,
|
link: `${BASE_URL}`,
|
||||||
title: config.siteName,
|
title: config.siteName,
|
||||||
description: config.longDescription,
|
description: config.description,
|
||||||
copyright: config.licenseUrl,
|
copyright: config.licenseUrl,
|
||||||
updated: new Date(RELEASE_TIMESTAMP),
|
updated: new Date(RELEASE_TIMESTAMP),
|
||||||
image: `${BASE_URL}${ogImage.src}`,
|
image: `${BASE_URL}${ogImage.src}`,
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import * as config from "../config";
|
import * as config from "../config";
|
||||||
import { BASE_URL } from "../config/constants";
|
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: new URL(BASE_URL),
|
||||||
title: {
|
title: {
|
||||||
template: `%s – ${config.siteName}`,
|
template: `%s – ${config.siteName}`,
|
||||||
default: `${config.siteName} – ${config.shortDescription}`,
|
default: `${config.siteName} – ${config.tagline}`,
|
||||||
},
|
},
|
||||||
description: config.longDescription,
|
description: config.description,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
siteName: config.siteName,
|
siteName: config.siteName,
|
||||||
title: {
|
title: {
|
||||||
template: "%s",
|
template: "%s",
|
||||||
default: `${config.siteName} – ${config.shortDescription}`,
|
default: `${config.siteName} – ${config.tagline}`,
|
||||||
},
|
},
|
||||||
url: "/",
|
url: "/",
|
||||||
locale: config.siteLocale?.replace("-", "_"),
|
locale: SITE_LOCALE?.replace("-", "_"),
|
||||||
type: "website",
|
type: "website",
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user