From 0d2ebd6033b8aa992d98ad6df14e936ed9d468c6 Mon Sep 17 00:00:00 2001 From: Tyler Mercer Date: Tue, 5 Dec 2023 20:15:48 -0700 Subject: [PATCH] Break out utils file Each function in its own file as a default export --- src/components/PostCard.astro | 3 +- src/components/SiteNavHeader.astro | 2 +- src/layouts/Post.astro | 11 +- src/pages/feeds/feed.json.ts | 7 +- src/pages/feeds/feed.xml.ts | 6 +- src/pages/index.astro | 5 +- .../og-images/[...collectionAndSlug].png.ts | 5 +- src/pages/og-images/_renderImage.ts | 2 +- src/pages/posts/[...post].astro | 13 +-- src/pages/posts/[category]/[page].astro | 6 +- src/pages/posts/[page].astro | 4 +- src/utils/checkCategory.ts | 5 + src/utils/countWords.ts | 6 + src/utils/filterOutDraftsIfProduction.ts | 10 ++ src/utils/firstFive.ts | 4 + src/utils/formatDateIso.ts | 4 + src/utils/formatPostDate.ts | 5 + src/utils/getCategory.ts | 8 ++ src/utils/getPostDate.ts | 10 ++ src/utils/isDraft.ts | 7 ++ src/utils/labelDrafts.ts | 11 ++ src/utils/renderMarkdown.ts | 17 +++ src/utils/sortByDate.ts | 8 ++ src/utils/toWithNextAndPrev.ts | 14 +++ src/utils/trimTrailingSlash.ts | 4 + src/utils/unMarkdown.ts | 3 + src/utils/utils.ts | 104 ------------------ 27 files changed, 156 insertions(+), 128 deletions(-) create mode 100644 src/utils/checkCategory.ts create mode 100644 src/utils/countWords.ts create mode 100644 src/utils/filterOutDraftsIfProduction.ts create mode 100644 src/utils/firstFive.ts create mode 100644 src/utils/formatDateIso.ts create mode 100644 src/utils/formatPostDate.ts create mode 100644 src/utils/getCategory.ts create mode 100644 src/utils/getPostDate.ts create mode 100644 src/utils/isDraft.ts create mode 100644 src/utils/labelDrafts.ts create mode 100644 src/utils/renderMarkdown.ts create mode 100644 src/utils/sortByDate.ts create mode 100644 src/utils/toWithNextAndPrev.ts create mode 100644 src/utils/trimTrailingSlash.ts create mode 100644 src/utils/unMarkdown.ts delete mode 100644 src/utils/utils.ts diff --git a/src/components/PostCard.astro b/src/components/PostCard.astro index d1ce7342..bbaa6dc6 100644 --- a/src/components/PostCard.astro +++ b/src/components/PostCard.astro @@ -1,7 +1,8 @@ --- import type { CollectionEntry } from "astro:content"; -import { getPostDate, formatPostDate } from "../utils/utils"; import renderInlineMarkdown from "../utils/renderInlineMarkdown"; +import formatPostDate from "../utils/formatPostDate"; +import getPostDate from "../utils/getPostDate"; export type Props = { post: CollectionEntry<"posts">; diff --git a/src/components/SiteNavHeader.astro b/src/components/SiteNavHeader.astro index a7642086..3135cc9f 100644 --- a/src/components/SiteNavHeader.astro +++ b/src/components/SiteNavHeader.astro @@ -1,5 +1,5 @@ --- -import { trimTrailingSlash } from "../utils/utils"; +import trimTrailingSlash from "../utils/trimTrailingSlash"; import Logo from "./Logo.astro"; const isCurrent = (path: string) => diff --git a/src/layouts/Post.astro b/src/layouts/Post.astro index 59ad61bb..82e1b999 100644 --- a/src/layouts/Post.astro +++ b/src/layouts/Post.astro @@ -3,15 +3,12 @@ import { type CollectionEntry } from "astro:content"; import Layout from "./Base.astro"; import MungedEmail from "../components/MungedEmail.astro"; import ShareLinks from "../components/ShareLinks.astro"; -import { - countWords, - formatPostDate, - getPostDate, - type WithNextAndPrev, -} from "../utils/utils"; import NextPrevLinks from "../components/NextPrevLinks.astro"; -import renderInlineMarkdown from "../utils/renderInlineMarkdown"; import PostDescription from "../components/PostDescription.astro"; +import type { WithNextAndPrev } from "../utils/toWithNextAndPrev"; +import countWords from "../utils/countWords"; +import formatPostDate from "../utils/formatPostDate"; +import getPostDate from "../utils/getPostDate"; export type Props = { post: WithNextAndPrev>; diff --git a/src/pages/feeds/feed.json.ts b/src/pages/feeds/feed.json.ts index c6c0b928..8068b370 100644 --- a/src/pages/feeds/feed.json.ts +++ b/src/pages/feeds/feed.json.ts @@ -1,6 +1,11 @@ import metadata from '../../content/_metadata'; import { getCollection } from 'astro:content'; -import { filterOutDraftsIfProduction, formatDateIso, getPostDate, sortByDate, renderMarkdown, getCategory } from '../../utils/utils'; +import formatDateIso from '../../utils/formatDateIso'; +import getPostDate from "../../utils/getPostDate"; +import sortByDate from "../../utils/sortByDate"; +import filterOutDraftsIfProduction from "../../utils/filterOutDraftsIfProduction"; +import getCategory from "../../utils/getCategory"; +import renderMarkdown from "../../utils/renderMarkdown"; export async function GET(context) { const blog = sortByDate(filterOutDraftsIfProduction(await getCollection('posts'))); diff --git a/src/pages/feeds/feed.xml.ts b/src/pages/feeds/feed.xml.ts index 5d2d17ae..f19f1b62 100644 --- a/src/pages/feeds/feed.xml.ts +++ b/src/pages/feeds/feed.xml.ts @@ -2,7 +2,11 @@ import rss from '@astrojs/rss'; import metadata from '../../content/_metadata'; import { getCollection } from 'astro:content'; -import { filterOutDraftsIfProduction, formatDateIso, getPostDate, renderMarkdown, sortByDate } from '../../utils/utils'; +import formatDateIso from '../../utils/formatDateIso'; +import getPostDate from "../../utils/getPostDate"; +import sortByDate from "../../utils/sortByDate"; +import filterOutDraftsIfProduction from "../../utils/filterOutDraftsIfProduction"; +import renderMarkdown from "../../utils/renderMarkdown"; export async function GET(context) { const blog = sortByDate(filterOutDraftsIfProduction(await getCollection('posts'))); diff --git a/src/pages/index.astro b/src/pages/index.astro index 66fb1f8d..476f4b30 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,8 +1,11 @@ --- import { getCollection } from "astro:content"; import Layout from "../layouts/Home.astro"; -import { filterOutDraftsIfProduction, firstFive, labelDrafts, sortByDate } from "../utils/utils"; import metadata from "../content/_metadata"; +import filterOutDraftsIfProduction from "../utils/filterOutDraftsIfProduction"; +import firstFive from "../utils/firstFive"; +import labelDrafts from "../utils/labelDrafts"; +import sortByDate from "../utils/sortByDate"; const { title, description } = metadata; diff --git a/src/pages/og-images/[...collectionAndSlug].png.ts b/src/pages/og-images/[...collectionAndSlug].png.ts index 9646d28e..b259853f 100644 --- a/src/pages/og-images/[...collectionAndSlug].png.ts +++ b/src/pages/og-images/[...collectionAndSlug].png.ts @@ -1,7 +1,10 @@ import { getCollection, getEntryBySlug, type ContentCollectionKey, type CollectionEntry } from "astro:content"; import type { APIRoute } from "astro"; -import { labelDrafts, sortByDate, filterOutDraftsIfProduction, getCategory } from "../../utils/utils"; +import sortByDate from "../../utils/sortByDate"; +import filterOutDraftsIfProduction from "../../utils/filterOutDraftsIfProduction"; +import labelDrafts from "../../utils/labelDrafts"; +import getCategory from "../../utils/getCategory"; import { renderImage } from "./_renderImage"; export async function getStaticPaths() { diff --git a/src/pages/og-images/_renderImage.ts b/src/pages/og-images/_renderImage.ts index 115ed352..b050effa 100644 --- a/src/pages/og-images/_renderImage.ts +++ b/src/pages/og-images/_renderImage.ts @@ -5,7 +5,7 @@ import sharp from "sharp"; import styles from './_styles.css?raw'; import faustinaRaw from '@fontsource/faustina/files/faustina-latin-400-normal.woff'; import figtreeRaw from '@fontsource/figtree/files/figtree-latin-700-normal.woff'; -import { formatPostDate } from "../../utils/utils"; +import formatPostDate from "../../utils/formatPostDate"; // They already are Buffers because of the custom rawFonts Vite plugin in astro.config.js, but TS doesn't know that const figtree = figtreeRaw as unknown as Buffer; diff --git a/src/pages/posts/[...post].astro b/src/pages/posts/[...post].astro index c8a4e09b..a5043f33 100644 --- a/src/pages/posts/[...post].astro +++ b/src/pages/posts/[...post].astro @@ -1,14 +1,11 @@ --- import Layout from "../../layouts/Post.astro"; import { type CollectionEntry, getCollection } from "astro:content"; -import { - filterOutDraftsIfProduction, - getCategory, - labelDrafts, - sortByDate, - toWithNextAndPrev, -type WithNextAndPrev, -} from "../../utils/utils"; +import filterOutDraftsIfProduction from "../../utils/filterOutDraftsIfProduction"; +import getCategory from "../../utils/getCategory"; +import labelDrafts from "../../utils/labelDrafts"; +import sortByDate from "../../utils/sortByDate"; +import toWithNextAndPrev, { type WithNextAndPrev } from "../../utils/toWithNextAndPrev"; export async function getStaticPaths() { const posts = await getCollection("posts"); diff --git a/src/pages/posts/[category]/[page].astro b/src/pages/posts/[category]/[page].astro index e89629b6..31c93e12 100644 --- a/src/pages/posts/[category]/[page].astro +++ b/src/pages/posts/[category]/[page].astro @@ -2,7 +2,11 @@ import type { GetStaticPaths } from "astro"; import { getCollection, type CollectionEntry } from "astro:content"; import Layout from "../../../layouts/Category.astro"; -import { checkCategory, filterOutDraftsIfProduction, getCategory, labelDrafts, sortByDate } from "../../../utils/utils"; +import checkCategory from "../../../utils/checkCategory"; +import filterOutDraftsIfProduction from "../../../utils/filterOutDraftsIfProduction"; +import getCategory from "../../../utils/getCategory"; +import labelDrafts from "../../../utils/labelDrafts"; +import sortByDate from "../../../utils/sortByDate"; export type Props = { page: { diff --git a/src/pages/posts/[page].astro b/src/pages/posts/[page].astro index 8bbc4274..d3071d3a 100644 --- a/src/pages/posts/[page].astro +++ b/src/pages/posts/[page].astro @@ -2,8 +2,10 @@ import type { GetStaticPaths } from "astro"; import { getCollection, type CollectionEntry } from "astro:content"; import Layout from "../../layouts/Category.astro"; -import { filterOutDraftsIfProduction, labelDrafts, sortByDate } from "../../utils/utils"; import category from '../../content/_allPostsCategory'; +import filterOutDraftsIfProduction from "../../utils/filterOutDraftsIfProduction"; +import labelDrafts from "../../utils/labelDrafts"; +import sortByDate from "../../utils/sortByDate"; export type Props = { page: { diff --git a/src/utils/checkCategory.ts b/src/utils/checkCategory.ts new file mode 100644 index 00000000..4ff2fa9e --- /dev/null +++ b/src/utils/checkCategory.ts @@ -0,0 +1,5 @@ +import { type CollectionEntry } from "astro:content"; + +export default function checkCategory(entry: CollectionEntry<'posts'>, knownCategorySlug: string) { + return entry.id.split('/').at(0) === knownCategorySlug; +} diff --git a/src/utils/countWords.ts b/src/utils/countWords.ts new file mode 100644 index 00000000..c592cb8d --- /dev/null +++ b/src/utils/countWords.ts @@ -0,0 +1,6 @@ +// Adapted from Nunjuck's wordcount filter implementation + +export default function countWords(str: string): number { + const words = (str) ? str.match(/\w+/g) : null; + return (words) ? words.length : 0; +} diff --git a/src/utils/filterOutDraftsIfProduction.ts b/src/utils/filterOutDraftsIfProduction.ts new file mode 100644 index 00000000..c148142b --- /dev/null +++ b/src/utils/filterOutDraftsIfProduction.ts @@ -0,0 +1,10 @@ +import { type CollectionEntry } from "astro:content"; +import isDraft from "./isDraft"; + +export default function filterOutDraftsIfProduction(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { + const isProduction = (import.meta.env.MODE === 'production'); + if (!isProduction) return entries; + return entries.filter( + (e: CollectionEntry<'posts'>) => !isDraft(e) + ); +} diff --git a/src/utils/firstFive.ts b/src/utils/firstFive.ts new file mode 100644 index 00000000..e4bdfd04 --- /dev/null +++ b/src/utils/firstFive.ts @@ -0,0 +1,4 @@ + +export default function firstFive(collection: any[]) { + return collection.slice(0, 5); +} diff --git a/src/utils/formatDateIso.ts b/src/utils/formatDateIso.ts new file mode 100644 index 00000000..ccaa13e5 --- /dev/null +++ b/src/utils/formatDateIso.ts @@ -0,0 +1,4 @@ + +export default function formatDateIso(date: Date) { + return date.toISOString(); +} diff --git a/src/utils/formatPostDate.ts b/src/utils/formatPostDate.ts new file mode 100644 index 00000000..3a5c0122 --- /dev/null +++ b/src/utils/formatPostDate.ts @@ -0,0 +1,5 @@ +import { DateTime } from "luxon"; + +export default function formatPostDate(date: Date) { + return DateTime.fromJSDate(date).toLocaleString(DateTime.DATE_MED); +} diff --git a/src/utils/getCategory.ts b/src/utils/getCategory.ts new file mode 100644 index 00000000..e2fb39a1 --- /dev/null +++ b/src/utils/getCategory.ts @@ -0,0 +1,8 @@ +import { getCollection, type CollectionEntry } from "astro:content"; + +export default async function getCategory(entry: CollectionEntry<'posts'>): Promise | undefined> { + const categories = await getCollection('categories'); + const category = entry.id.split('/').at(0); + if (!category) return undefined; + return categories.find(c => c.id === category); +} diff --git a/src/utils/getPostDate.ts b/src/utils/getPostDate.ts new file mode 100644 index 00000000..9c425871 --- /dev/null +++ b/src/utils/getPostDate.ts @@ -0,0 +1,10 @@ +import { type CollectionEntry } from "astro:content"; + +const now = new Date(); + +export default function getPostDate(entry: CollectionEntry<'posts'>) { + if (entry.data.date) { + return entry.data.date; + } + else return now; +} diff --git a/src/utils/isDraft.ts b/src/utils/isDraft.ts new file mode 100644 index 00000000..dfc31a51 --- /dev/null +++ b/src/utils/isDraft.ts @@ -0,0 +1,7 @@ +import { type CollectionEntry } from "astro:content"; + +const now = new Date(); + +export default function isDraft(entry: CollectionEntry<'posts'>): boolean { + return !(entry.data.date && entry.data.date < now); +} diff --git a/src/utils/labelDrafts.ts b/src/utils/labelDrafts.ts new file mode 100644 index 00000000..b9325ed6 --- /dev/null +++ b/src/utils/labelDrafts.ts @@ -0,0 +1,11 @@ +import { type CollectionEntry } from "astro:content"; +import isDraft from "./isDraft"; + + +export default function labelDrafts(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { + const label = `[Draft]`; + return entries.map((e: CollectionEntry<'posts'>) => { + e.data.title = (isDraft(e) && !e.data.title.startsWith(label)) ? `${label} ${e.data.title}` : e.data.title; + return e; + }); +} diff --git a/src/utils/renderMarkdown.ts b/src/utils/renderMarkdown.ts new file mode 100644 index 00000000..cf4cdce4 --- /dev/null +++ b/src/utils/renderMarkdown.ts @@ -0,0 +1,17 @@ +import sanitizeHtml from 'sanitize-html'; +import MarkdownIt from 'markdown-it'; +import anchor from 'markdown-it-anchor'; +import footnote from 'markdown-it-footnote'; + +const parser = new MarkdownIt({ + html: true, + typographer: true +}).use(anchor, { + permalink: anchor.permalink.headerLink({ + safariReaderFix: true + }) +}).use(footnote); + +export default function renderMarkdown(markdown: string) { + return sanitizeHtml(parser.render(markdown)); +} diff --git a/src/utils/sortByDate.ts b/src/utils/sortByDate.ts new file mode 100644 index 00000000..52077dbd --- /dev/null +++ b/src/utils/sortByDate.ts @@ -0,0 +1,8 @@ +import { type CollectionEntry } from "astro:content"; +import getPostDate from "./getPostDate"; + + +export default function sortByDate(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { + return entries.sort( + (a, b) => getPostDate(b).getTime() - getPostDate(a).getTime()); +} diff --git a/src/utils/toWithNextAndPrev.ts b/src/utils/toWithNextAndPrev.ts new file mode 100644 index 00000000..f72f1a34 --- /dev/null +++ b/src/utils/toWithNextAndPrev.ts @@ -0,0 +1,14 @@ + +export type WithNextAndPrev = { + item: T, + next?: T, + prev?: T +} + +export default function toWithNextAndPrev(items: T[], reverse: boolean = false): WithNextAndPrev[] { + return items.map((t, i, a) => ({ + item: t, + next: reverse ? a[i - 1] : a[i + 1], + prev: reverse ? a[i + 1] : a[i - 1] + })); +} diff --git a/src/utils/trimTrailingSlash.ts b/src/utils/trimTrailingSlash.ts new file mode 100644 index 00000000..8c0c30ad --- /dev/null +++ b/src/utils/trimTrailingSlash.ts @@ -0,0 +1,4 @@ + +export default function trimTrailingSlash(str: string): string { + return str.replace(/\/$/, ""); +} diff --git a/src/utils/unMarkdown.ts b/src/utils/unMarkdown.ts new file mode 100644 index 00000000..5f3cc6fc --- /dev/null +++ b/src/utils/unMarkdown.ts @@ -0,0 +1,3 @@ +export default function unMarkdown(raw: string): string { + return raw?.replace(/[\*_`]/g, '') +} \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts deleted file mode 100644 index 811241e9..00000000 --- a/src/utils/utils.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { getCollection, type CollectionEntry } from "astro:content"; -import { DateTime } from "luxon"; -import MarkdownIt from 'markdown-it'; -import sanitizeHtml from 'sanitize-html'; -import anchor from 'markdown-it-anchor'; -import footnote from 'markdown-it-footnote'; - -const parser = new MarkdownIt({ - html: true, - typographer: true -}).use(anchor, { - permalink: anchor.permalink.headerLink({ - safariReaderFix: true - }) -}).use(footnote); - -export function firstFive(collection: any[]) { - return collection.slice(0, 5); -} - -export function formatDateIso(date: Date) { - return date.toISOString(); -} - -export function formatPostDate(date: Date) { - return DateTime.fromJSDate(date).toLocaleString(DateTime.DATE_MED); -} - -const now = new Date(); - -export function getPostDate(entry: CollectionEntry<'posts'>) { - if (entry.data.date) { - return entry.data.date; - } - else return now; -} - -export function sortByDate(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { - return entries.sort( - (a, b) => getPostDate(b).getTime() - getPostDate(a).getTime()); -} - -export function isDraft(entry: CollectionEntry<'posts'>): boolean { - return !(entry.data.date && entry.data.date < now); -} - -export function filterOutDraftsIfProduction(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { - const isProduction = (import.meta.env.MODE === 'production'); - if (!isProduction) return entries; - return entries.filter( - (e: CollectionEntry<'posts'>) => !isDraft(e) - ); -} - -export function labelDrafts(entries: CollectionEntry<'posts'>[]): CollectionEntry<'posts'>[] { - const label = `[Draft]`; - return entries.map((e: CollectionEntry<'posts'>) => { - e.data.title = (isDraft(e) && !e.data.title.startsWith(label)) ? `${label} ${e.data.title}` : e.data.title; - return e; - }); -} - -export async function getCategory(entry: CollectionEntry<'posts'>): Promise | undefined> { - const categories = await getCollection('categories'); - const category = entry.id.split('/').at(0); - if (!category) return undefined; - return categories.find(c => c.id === category); -} - -export function checkCategory(entry: CollectionEntry<'posts'>, knownCategorySlug: string) { - return entry.id.split('/').at(0) === knownCategorySlug; -} - -export function renderMarkdown(markdown: string) { - return sanitizeHtml(parser.render(markdown)); -} - -export type WithNextAndPrev = { - item: T, - next?: T, - prev?: T -} - -export function toWithNextAndPrev(items: T[], reverse: boolean = false): WithNextAndPrev[] { - return items.map((t, i, a) => ({ - item: t, - next: reverse ? a[i - 1] : a[i + 1], - prev: reverse ? a[i + 1] : a[i - 1] - })); -} - -// Adapted from Nunjuck's wordcount filter implementation -export function countWords(str: string): number { - const words = (str) ? str.match(/\w+/g) : null; - return (words) ? words.length : 0; -} - -export function trimTrailingSlash(str: string): string { - return str.replace(/\/$/, ""); -} - -export function unMarkdown(raw: string): string { - return raw?.replace(/[\*_`]/g, '') -} \ No newline at end of file