1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-09-18 16:05:33 -04:00

fix post title encoding

This commit is contained in:
2025-03-14 15:08:00 -04:00
parent 3932660acc
commit 6e572a8f48
7 changed files with 29 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
import Image from "next/image";
import clsx from "clsx"; import clsx from "clsx";
import Link from "../Link"; import Link from "../Link";
import Image from "../Image";
import Menu from "../Menu"; import Menu from "../Menu";
import * as config from "../../lib/config"; import * as config from "../../lib/config";
import { MAX_WIDTH } from "../../lib/config/constants"; import { MAX_WIDTH } from "../../lib/config/constants";

View File

@@ -0,0 +1,5 @@
.image {
display: block;
margin: 1em auto;
max-width: 100%;
}

View File

@@ -1,11 +1,14 @@
import NextImage from "next/image"; import NextImage from "next/image";
import clsx from "clsx";
import { MAX_WIDTH } from "../../lib/config/constants"; import { MAX_WIDTH } from "../../lib/config/constants";
import type { ComponentPropsWithoutRef } from "react"; import type { ComponentPropsWithoutRef } from "react";
import type { StaticImageData } from "next/image"; import type { StaticImageData } from "next/image";
import styles from "./Image.module.css";
export type ImageProps = ComponentPropsWithoutRef<typeof NextImage>; export type ImageProps = ComponentPropsWithoutRef<typeof NextImage>;
const Image = ({ src, height, width, quality, placeholder, style, ...rest }: ImageProps) => { const Image = ({ src, height, width, placeholder, className, ...rest }: ImageProps) => {
const constrainWidth = (width?: number | `${number}`) => { const constrainWidth = (width?: number | `${number}`) => {
if (!width) return MAX_WIDTH; if (!width) return MAX_WIDTH;
@@ -16,16 +19,11 @@ const Image = ({ src, height, width, quality, placeholder, style, ...rest }: Ima
src, src,
height, height,
width: constrainWidth(width || (src as StaticImageData).width), width: constrainWidth(width || (src as StaticImageData).width),
quality: quality || 75,
placeholder: placeholder || (typeof src === "string" ? "empty" : "blur"), placeholder: placeholder || (typeof src === "string" ? "empty" : "blur"),
style: {
height: "auto",
...style,
},
...rest, ...rest,
}; };
return <NextImage {...imageProps} />; return <NextImage className={clsx(styles.image, className)} {...imageProps} />;
}; };
export default Image; export default Image;

View File

@@ -3,7 +3,7 @@ export const siteName = "Jake Jarvis";
export const siteLocale = "en-US"; export const siteLocale = "en-US";
export const timeZone = "America/New_York"; // https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List export const timeZone = "America/New_York"; // https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
export const onionDomain = "jarvis2i2vp4j4tbxjogsnqdemnte5xhzyi7hziiyzxwge3hzmh57zad.onion"; export const onionDomain = "jarvis2i2vp4j4tbxjogsnqdemnte5xhzyi7hziiyzxwge3hzmh57zad.onion";
export const shortDescription = "Front-End Web Developer in Boston; MA"; export const shortDescription = "Front-End Web Developer in Boston, MA";
export const longDescription = export const longDescription =
"Hi there! I'm a frontend web developer based in Boston, Massachusetts specializing in the JAMstack, modern JavaScript frameworks, and progressive web apps."; "Hi there! I'm a frontend web developer based in Boston, Massachusetts specializing in the JAMstack, modern JavaScript frameworks, and progressive web apps.";
export const license = "Creative Commons Attribution 4.0 International"; export const license = "Creative Commons Attribution 4.0 International";

View File

@@ -2,6 +2,7 @@ import path from "path";
import glob from "fast-glob"; import glob from "fast-glob";
import pMap from "p-map"; import pMap from "p-map";
import pMemoize from "p-memoize"; import pMemoize from "p-memoize";
import { decode } from "html-entities";
import { formatDate } from "./format-date"; import { formatDate } from "./format-date";
import { BASE_URL, POSTS_DIR } from "../config/constants"; import { BASE_URL, POSTS_DIR } from "../config/constants";
@@ -32,7 +33,7 @@ export const getFrontMatter = async (slug: string): Promise<FrontMatter> => {
// allow *very* limited markdown to be used in post titles // allow *very* limited markdown to be used in post titles
const parseTitle = async (title: string, allowedTags: string[] = []): Promise<string> => { const parseTitle = async (title: string, allowedTags: string[] = []): Promise<string> => {
return String( const newTitle = (
await unified() await unified()
.use(remarkParse) .use(remarkParse)
.use(remarkSmartypants, { .use(remarkSmartypants, {
@@ -45,7 +46,10 @@ export const getFrontMatter = async (slug: string): Promise<FrontMatter> => {
.use(rehypeSanitize, { tagNames: allowedTags }) .use(rehypeSanitize, { tagNames: allowedTags })
.use(rehypeStringify) .use(rehypeStringify)
.process(title) .process(title)
); ).toString();
// assume if we don't want any html titles then we don't want encoded html entities either
return allowedTags.length === 0 ? decode(newTitle) : newTitle;
}; };
// process title as both plain and stylized // process title as both plain and stylized
@@ -57,8 +61,9 @@ export const getFrontMatter = async (slug: string): Promise<FrontMatter> => {
// return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content // return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content
return { return {
...(frontmatter as Partial<FrontMatter>), ...(frontmatter as Partial<FrontMatter>),
// zero markdown title: // plain title without html or markdown syntax:
title, title,
// stylized title with limited html tags:
htmlTitle, htmlTitle,
slug, slug,
date: formatDate(frontmatter.date), // validate/normalize the date string provided from front matter date: formatDate(frontmatter.date), // validate/normalize the date string provided from front matter

View File

@@ -35,6 +35,7 @@
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"feed": "^4.2.2", "feed": "^4.2.2",
"geist": "^1.3.1", "geist": "^1.3.1",
"html-entities": "^2.5.2",
"lucide-react": "0.481.0", "lucide-react": "0.481.0",
"modern-normalize": "^3.0.1", "modern-normalize": "^3.0.1",
"next": "15.3.0-canary.8", "next": "15.3.0-canary.8",

8
pnpm-lock.yaml generated
View File

@@ -59,6 +59,9 @@ importers:
geist: geist:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1(next@15.3.0-canary.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) version: 1.3.1(next@15.3.0-canary.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
html-entities:
specifier: ^2.5.2
version: 2.5.2
lucide-react: lucide-react:
specifier: 0.481.0 specifier: 0.481.0
version: 0.481.0(react@19.0.0) version: 0.481.0(react@19.0.0)
@@ -1944,6 +1947,9 @@ packages:
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
engines: {node: ^16.14.0 || >=18.0.0} engines: {node: ^16.14.0 || >=18.0.0}
html-entities@2.5.2:
resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==}
html-escaper@2.0.2: html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -5542,6 +5548,8 @@ snapshots:
dependencies: dependencies:
lru-cache: 10.4.3 lru-cache: 10.4.3
html-entities@2.5.2: {}
html-escaper@2.0.2: {} html-escaper@2.0.2: {}
html-tags@3.3.1: {} html-tags@3.3.1: {}