diff --git a/.gitignore b/.gitignore index 44aab12..5df0761 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules build *.js !static/*.js -/posts/_draft_*.md \ No newline at end of file +/posts/_draft_*.md +nohup.out diff --git a/index.ts b/index.ts index a91fa4e..03fa42c 100644 --- a/index.ts +++ b/index.ts @@ -4,6 +4,7 @@ import { parse_md_to_html } from 'makoto'; import { Renderer } from './ryuji.js'; import { Builder } from './saki.js'; import _posts_metadata from './posts/_metadata.json'; +import _site_info from './site_info.json'; export interface PostMetadata { title: string, @@ -20,6 +21,18 @@ export interface Post extends PostMetadata { tags_exist: boolean, } +export interface RSSPost extends PostMetadata { + url: string, + last_updated: string, + html: string, +} + +export interface SiteInfo { + title: string, + url: string, + icon: string, +} + let renderer: Renderer = new Renderer("templates", "components"); let builder: Builder = new Builder(); @@ -28,7 +41,7 @@ let posts_metadata: PostMetadata[] = Object.values(_posts_metadata); builder.serve_static_folder("static"); //home page -builder.serve_template(renderer, "/", "index.html", { +builder.serve_template(renderer, "/", "index", { posts: posts_metadata, }); @@ -75,7 +88,7 @@ for (let i=0; i < posts_metadata.length; i++) { ); } -builder.serve_templates(renderer, posts_serve_paths, "post.html", posts_vars); +builder.serve_templates(renderer, posts_serve_paths, "post", posts_vars); //tags @@ -91,4 +104,46 @@ for (let i=0; i < tags.length; i++) { }); } -builder.serve_templates(renderer, tags_serve_paths, "tags.html", tags_vars); +builder.serve_templates(renderer, tags_serve_paths, "tags", tags_vars); + +//build rss feed +let first_posts: PostMetadata[] = posts_metadata.slice(0, 5); //not truly the recents, actually the first 5 posts in the json file, which is decided by me and usually the most recent posts + +const site_info: SiteInfo = _site_info; + +let posts_rss: RSSPost[] = first_posts.map((post) => { + //get url + let url: string = `${site_info.url}/posts/${post.slug}`; + //get last_updated + let date_parts: number[] = post.date.split("/").map((p) => Number(p)); // dd/mm/yyyy + let date: Date = new Date(); + date.setUTCFullYear(date_parts[2]); + date.setUTCMonth(date_parts[1]-1, date_parts[0]); + date.setUTCHours(0, 0, 0, 0); + let iso_string: string = date.toISOString(); + let last_updated: string = iso_string.slice(0, iso_string.length-1)+"+00:00"; //remove the "Z" in iso string + //get html + let post_md_path: string = path.join(__dirname, `/posts/${post.filename}.md`); + let md: string = readFileSync(post_md_path, "utf-8").replaceAll("\r", ""); + let html: string = parse_md_to_html(md); + //turn into rsspost + return { + ...post, + url, + last_updated, + html, + } +}); + +//might leak what side of the planet youre on +let now: Date = new Date(); +now.setUTCHours(0, 0, 0, 0); +let global_iso_string: string = now.toISOString(); +let global_last_updated: string = global_iso_string.slice(0, global_iso_string.length-1)+"+00:00"; //remove the "Z" in iso string + +builder.serve_template(renderer, "/atom.xml", "atom.xml", { + site_info, + recent_posts: posts_rss, + last_updated: global_last_updated, +}); + diff --git a/preview.ts b/preview.ts index 8a0b1d4..2aeadb2 100644 --- a/preview.ts +++ b/preview.ts @@ -30,6 +30,9 @@ createServer((req, res) => { case "js": content_type = "text/javascript"; break; + case "xml": + content_type = "text/xml"; + break; case "png": case "ico": content_type = "image/png"; diff --git a/ryuji.ts b/ryuji.ts index defd8ea..63df44d 100644 --- a/ryuji.ts +++ b/ryuji.ts @@ -89,7 +89,10 @@ export class Renderer { if (recursion_layer > 5) throw Error("Components more than 5 layers deep, components may be referencing each other in infinite loop."); if (typeof exp_parts[1] !== "string") throw Error("`component:` statement missing component file name afterwards"); let file_name: string = exp_parts[1]; - rendered += this.render_template(Renderer.concat_path(this.components_dir, `${file_name}${this.file_extension}`), vars, recursion_layer+1); + if (!file_name.includes(".")) { + file_name += this.file_extension; + } + rendered += this.render_template(Renderer.concat_path(this.components_dir, file_name), vars, recursion_layer+1); } else if (exp_parts[0] === "for") { if (for_loops[for_loops.length-1]?.index === index) { //for loop already exists, just continue and do nothing @@ -265,6 +268,9 @@ export class Renderer { } render_template(template_name: string, vars?: any, recursion_layer: number=0): string { + if (!template_name.includes(".")) { + template_name += this.file_extension; + } let path_to_template: string = Renderer.concat_path(this.templates_dir, template_name); const template_contents: string = readFileSync(path_to_template, "utf-8"); return this.render(template_contents, vars, recursion_layer); diff --git a/site_info.json b/site_info.json new file mode 100644 index 0000000..034b594 --- /dev/null +++ b/site_info.json @@ -0,0 +1,5 @@ +{ + "title": "prussiafan.club/hedgeblog", + "url": "https://www.prussiafan.club/", + "icon": "https://www.prussiafan.club/favicon.ico" +} diff --git a/templates/atom.xml b/templates/atom.xml new file mode 100644 index 0000000..b8ce75a --- /dev/null +++ b/templates/atom.xml @@ -0,0 +1,10 @@ + + [[ site_info.title ]] + + [[ site_info.url ]] + [[ last_updated ]] + [[ site_info.icon ]] + [[ for:recent_posts:post ]] + [[ component:post_entry.xml ]] + [[ endfor ]] + diff --git a/templates/components/post_entry.xml b/templates/components/post_entry.xml new file mode 100644 index 0000000..030d9ae --- /dev/null +++ b/templates/components/post_entry.xml @@ -0,0 +1,12 @@ + + [[ post.title ]] + + [[ post.url ]] + [[ post.last_updated ]] + + [[ post.author ]] + + + [[ post.html ]] + +