feat: initial commit

This commit is contained in:
Cory Dransfeldt 2024-11-16 16:43:07 -08:00
commit 0ff7457679
No known key found for this signature in database
192 changed files with 24379 additions and 0 deletions

View file

@ -0,0 +1,111 @@
import { convert } from "html-to-text";
import { format } from "date-fns-tz";
import markdownIt from "markdown-it";
import markdownItAnchor from "markdown-it-anchor";
import markdownItFootnote from "markdown-it-footnote";
import hljs from "highlight.js";
import "highlight.js/styles/github-dark.min.css";
import truncateHtml from "truncate-html";
// arrays
export const shuffleArray = (array) => {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
};
// countries
const regionNames = new Intl.DisplayNames(["en"], { type: "region" });
export const getCountryName = (countryCode) => {
try {
return regionNames.of(countryCode.trim()) || countryCode.trim();
} catch {
return countryCode.trim();
}
};
export const parseCountryField = (countryField) => {
if (!countryField) return null;
const delimiters = [",", "/", "&", "and"];
return delimiters
.reduce(
(countries, delimiter) =>
countries.flatMap((country) => country.split(delimiter)),
[countryField]
)
.map(getCountryName)
.join(", ");
};
// html
export const htmlTruncate = (content, limit = 50) =>
truncateHtml(content, limit, {
byWords: true,
ellipsis: "...",
});
export const htmlToText = (html) =>
convert(html, {
wordwrap: false,
selectors: [
{ selector: "a", options: { ignoreHref: true } },
{ selector: "h1", options: { uppercase: false } },
{ selector: "h2", options: { uppercase: false } },
{ selector: "h3", options: { uppercase: false } },
{ selector: "*", format: "block" },
],
});
export const escapeHtml = (input) =>
typeof input === "string"
? input.replace(/[&<>"']/g, (char) => {
const map = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;",
};
return map[char];
})
: "";
// markdown
const markdown = markdownIt({
html: true,
linkify: true,
breaks: true,
highlight: (code, lang) => {
if (lang && hljs.getLanguage(lang))
return hljs.highlight(code, { language: lang }).value;
return hljs.highlightAuto(code).value;
},
});
markdown
.use(markdownItAnchor, {
level: [1, 2],
permalink: markdownItAnchor.permalink.headerLink({
safariReaderFix: true,
}),
})
.use(markdownItFootnote);
export const md = (string) => markdown.render(string);
// urls
export const encodeAmp = (url) => url.replace(/&/g, "&amp;");
export const removeTrailingSlash = (url) => url.replace(/\/$/, "");
export const isExcludedPath = (path, exclusions) =>
exclusions.some((exclusion) => path.includes(exclusion));
// dates
export const dateToRFC822 = (date) =>
format(date, "EEE, dd MMM yyyy HH:mm:ss XXX", {
timeZone: "America/Los_Angeles",
});

View file

@ -0,0 +1,73 @@
import countries from "i18n-iso-countries";
import enLocale from "i18n-iso-countries/langs/en.json";
countries.registerLocale(enLocale);
export const filterBooksByStatus = (books, status) =>
books.filter((book) => book["status"] === status);
export const findFavoriteBooks = (books) =>
books.filter((book) => book["favorite"] === true);
export const bookYearLinks = (years) =>
years
.sort((a, b) => b["value"] - a["value"])
.map(
(year, index) =>
`<a href="/books/years/${year["value"]}">${year["value"]}</a>${
index < years.length - 1 ? " / " : ""
}`
)
.join("");
export const mediaLinks = (data, type, count = 10) => {
if (!data || !type) return "";
const dataSlice = data.slice(0, count);
if (dataSlice.length === 0) return null;
const buildLink = (item) => {
switch (type) {
case "genre":
return `<a href="${item["genre_url"]}">${item["genre_name"]}</a>`;
case "artist":
return `<a href="${item["url"]}">${item["name"]}</a>`;
case "book":
return `<a href="${item["url"]}">${item["title"]}</a>`;
default:
return "";
}
};
if (dataSlice.length === 1) return buildLink(dataSlice[0]);
const links = dataSlice.map(buildLink);
const allButLast = links.slice(0, -1).join(", ");
const last = links[links.length - 1];
return `${allButLast} and ${last}`;
};
export const parseCountries = (input) => {
if (!input) return null;
const countryCodes = input
.split(/\s+and\s+|,\s*|\s+/)
.map((code) => code.trim().toUpperCase())
.filter((code) => code.length > 0);
const countryNames = countryCodes.map((code) => {
const countryName = countries.getName(code, "en");
if (!countryName) {
console.warn(`Country code "${code}" is not valid.`);
return null;
}
return countryName;
});
return countryNames.filter((name) => name !== null).join(", ");
};
export const isbnRegex = /^\/books\/(\d{10}|\d{13})$/;