justin.deal/src/lib/utils.ts

105 lines
3.4 KiB
TypeScript

import fs from "node:fs/promises";
import { GLOBAL } from "./variables";
type MarkdownData<T extends object> = {
frontmatter: T;
file: string;
url: string;
};
/**
* This function processes the content of a directory and returns an array of processed content.
* It takes a content type, a function to process the content, and an optional directory.
* If no directory is provided, it defaults to the current working directory.
*
* @param contentType the type of content to process
* @param processFn the function to process the content
* @param dir the directory to process the content from
* @returns a promise that resolves to an array of processed content
*/
export const processContentInDir = async <T extends object, K>(
contentType: "projects" | "blog",
processFn: (data: MarkdownData<T>) => K,
dir: string = process.cwd(),
) => {
const files = await fs.readdir(dir + `/src/pages/${contentType}`);
const markdownFiles = files
.filter((file: string) => file.endsWith(".md"))
.map((file) => file.split(".")[0]);
const readMdFileContent = async (file: string) => {
if (contentType === "projects") {
const content = import.meta
.glob(`/src/pages/projects/*.md`)
[`/src/pages/projects/${file}.md`]();
const data = (await content) as {
frontmatter: T;
file: string;
url: string;
};
return processFn(data);
} else {
const content = import.meta
.glob(`/src/pages/blog/*.md`)
[`/src/pages/blog/${file}.md`]();
const data = (await content) as {
frontmatter: T;
file: string;
url: string;
};
return processFn(data);
}
};
return await Promise.all(markdownFiles.map(readMdFileContent));
};
/**
* Shortens a string by removing words at the end until it fits within a certain length.
* @param content the content to shorten
* @param maxLength the maximum length of the shortened content (default is 20)
* @returns a shortened version of the content
*/
export const getShortDescription = (content: string, maxLength = 20) => {
const splitByWord = content.split(" ");
const length = splitByWord.length;
return length > maxLength ? splitByWord.slice(0, maxLength).join(" ") + "..." : content;
};
/**
* Processes the date of an article and returns a string representing the processed date.
* @param timestamp the timestamp to process
* @returns a string representing the processed timestamp
*/
export const processArticleDate = (timestamp: string) => {
const date = new Date(timestamp);
const monthSmall = date.toLocaleString("default", { month: "short" });
const day = date.getDate();
const year = date.getFullYear();
return `${monthSmall} ${day}, ${year}`;
};
/**
* Generates a source URL for a content item. The URL is used in meta tags and social media cards.
* @param sourceUrl the source URL of the content
* @param contentType the type of content (either "projects" or "blog")
* @returns a string representing the source URL with the appropriate domain
*/
export const generateSourceUrl = (
sourceUrl: string,
contentType: "projects" | "blog",
) => {
return `${GLOBAL.rootUrl}/${contentType}/${sourceUrl}`;
};
export const countTags = (tags: string[]) => {
const tagCount: Record<string, number> = {};
tags.forEach((tag) => {
if (tagCount[tag]) {
tagCount[tag]++;
} else {
tagCount[tag] = 1;
}
});
return tagCount;
}