146 lines
4.0 KiB
JavaScript
146 lines
4.0 KiB
JavaScript
import { stringifyEntities } from 'stringify-entities';
|
|
import * as fs from 'fs/promises';
|
|
import config from '$lib/config';
|
|
|
|
/**
|
|
* @param {Record<string, any>} properties
|
|
*/
|
|
function renderProperties(properties) {
|
|
if (Object.keys(properties).length === 0) return '';
|
|
return ' ' + Object.entries(properties)
|
|
.filter(([k, v]) => k !== '' && v !== '')
|
|
.map(([k, v]) =>
|
|
v ? `${k}=${typeof v === 'string' ? `"${stringifyEntities(v)}"` : v.toString()}` : k
|
|
).join(' ');
|
|
}
|
|
|
|
/**
|
|
* @param {ASTMap} ast
|
|
* @returns {string}
|
|
*/
|
|
// todo: obliterate from orbit
|
|
export function renderASTMap(ast) {
|
|
switch (ast.type) {
|
|
case 'root':
|
|
return ast.children.map(c => renderASTMap(c)).join('')
|
|
case 'element':
|
|
if (ast.tagName === 'a') {
|
|
ast.properties.target = '_blank';
|
|
ast.properties.rel = 'noreferrer noopener';
|
|
}
|
|
if (ast.properties.id && ast.properties.id.includes('cohost-blogger-ignore')) return '';
|
|
return `<${ast.tagName}${renderProperties(ast.properties)}>${ast.children.map(c => renderASTMap(c)).join('')}</${ast.tagName}>`;
|
|
case 'text':
|
|
return stringifyEntities(ast.value);
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
const COHOST_API_URI = 'https://cohost.org/api/v1/trpc/';
|
|
/**
|
|
* @param {string} route
|
|
* @param {Record<string, any>} input
|
|
*/
|
|
export async function trpcRequest(route, input) {
|
|
const url = new URL(COHOST_API_URI + route);
|
|
if (input) url.searchParams.set('input', JSON.stringify(input));
|
|
const data = await (await fetch(url)).json();
|
|
return data;
|
|
}
|
|
|
|
const PAGES_PER_POST = 20;
|
|
|
|
/**
|
|
* @param {number} page
|
|
* @returns {Promise<Post[]>}
|
|
*/
|
|
export async function fetchAllPosts(page = 0) {
|
|
const data = await trpcRequest('posts.getPostsTagged', {
|
|
projectHandle: config.handle,
|
|
tagSlug: config.tag,
|
|
page: page
|
|
});
|
|
|
|
let posts = data.result.data.items;
|
|
|
|
if (data.result.data.nItems >= PAGES_PER_POST) {
|
|
posts = [...posts, ...(await fetchAllPosts(page + 1))]
|
|
}
|
|
|
|
return posts;
|
|
}
|
|
|
|
/**
|
|
* @returns {Promise<Post[]>}
|
|
*/
|
|
async function getPostsUncached() {
|
|
return await fetchAllPosts();
|
|
//return JSON.parse(await fs.readFile('src/testPosts.json', 'utf8')).filter(post => post.tags.includes('cohost-blogger'));
|
|
}
|
|
|
|
// this technically only stores the preview data - the posts on the actual pages are always fetched
|
|
// however there is no way to fetch a specified amount of info, so cache it is
|
|
let postCache = {
|
|
/** @type {Post[]} **/
|
|
posts: [],
|
|
refreshed: -1
|
|
}
|
|
const CACHE_INVALID_PERIOD = 60 * 1000;
|
|
|
|
/**
|
|
* @returns {Promise<Post[]>}
|
|
*/
|
|
export async function getPosts() {
|
|
const timeSinceCache = Date.now() - postCache.refreshed;
|
|
if (timeSinceCache > CACHE_INVALID_PERIOD) {
|
|
postCache.posts = await getPostsUncached();
|
|
postCache.refreshed = Date.now();
|
|
}
|
|
|
|
return postCache.posts;
|
|
}
|
|
|
|
/**
|
|
* @param {Post} post
|
|
*/
|
|
export function getPostImages(post) {
|
|
return post.blocks.filter(block => block.type === 'attachment').map(block => block.attachment);
|
|
}
|
|
const COMMENT_REGEX = /^\s*<!--\s*@cohost-blogger-meta\s+([\S\s]+)\s*-->\s*$/;
|
|
/**
|
|
* @param {Post} post
|
|
* @returns {Record<string, string>}
|
|
*/
|
|
export function getPostMetadata(post) {
|
|
return post.blocks
|
|
.filter(block => block.type === 'markdown')
|
|
.map(block => block.markdown.content)
|
|
.map(text => COMMENT_REGEX.exec(text)).filter(res => res !== null).map(res => res[1])
|
|
.reduce((lines, comment) => [...lines, ...comment.split('\n')], [])
|
|
.map(line => line.trim())
|
|
.filter(line => line.length > 0)
|
|
.reduce((properties, line) => {
|
|
properties[line.split(':')[0].trim()] = line.split(':')[1].trim();
|
|
return properties;
|
|
}, {});
|
|
}
|
|
|
|
/**
|
|
* @param {Post} post
|
|
* @returns {Date | null}
|
|
*/
|
|
export function getPostPublishDate(post) {
|
|
const meta = getPostMetadata(post);
|
|
if (meta['published-at']) return new Date(meta['published-at']);
|
|
if (post.publishedAt) return new Date(post.publishedAt);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {Post} post
|
|
* @returns {string}
|
|
*/
|
|
export function getPostSlug(post) {
|
|
return getPostMetadata(post).slug || post.filename;
|
|
} |