diff options
Diffstat (limited to 'app/builder')
| -rw-r--r-- | app/builder/buildProject.ts | 13 | ||||
| -rw-r--r-- | app/builder/discoverFeed.ts | 67 | ||||
| -rw-r--r-- | app/builder/pageDirectory.ts | 50 |
3 files changed, 130 insertions, 0 deletions
diff --git a/app/builder/buildProject.ts b/app/builder/buildProject.ts index 700e0ec..2f7aec4 100644 --- a/app/builder/buildProject.ts +++ b/app/builder/buildProject.ts @@ -5,6 +5,7 @@ import path from 'path'; import { logger } from '../logger.js'; import glob from 'glob'; import { process as processCss } from './processCss.js'; +import { discoverFeed } from './discoverFeed.js'; export async function buildPages(verbose: boolean = true): Promise<{ success: boolean, errors: number, pageDirectory: PageDirectory}> { // Recreate output directory @@ -44,6 +45,18 @@ export async function buildPages(verbose: boolean = true): Promise<{ success: bo if (verbose) logger.info(`Rendered ${pagesRendered} of ${pagesCount} pages.`); + // Discover feeds + if (verbose) logger.info(`Discovering feeds...`); + const feeds = pageDirectory.getFeeds(); + for (const feed of feeds) { + try { + await discoverFeed(feed, pageDirectory); + } catch (e) { + logger.error(`Failed to discover feed ${feed.title}: ${e.message}`); + } + } + + //TODO move to util const ensureParentDirExists = (file: string) => { const joinedOutputPath = path.join(process.env.OUTPUT_DIR, 'static', file); diff --git a/app/builder/discoverFeed.ts b/app/builder/discoverFeed.ts new file mode 100644 index 0000000..5ea1a16 --- /dev/null +++ b/app/builder/discoverFeed.ts @@ -0,0 +1,67 @@ +import path from "path"; +import fs from "fs"; +import { Feed, PageDirectory } from "./pageDirectory.js"; +import { logger } from "../logger.js"; + +export async function discoverFeed(feed: Feed, pageDirectory: PageDirectory): Promise<boolean> { + const entries = []; + + const titleConfigPath = feed.paramStrategy?.title?.from || "title"; + const updatedConfigPath = feed.paramStrategy?.date?.from || "date"; + const descriptionConfigPath = feed.paramStrategy?.description?.from || "description"; + //todo support actual discovery strategies + + for (const page of pageDirectory.getPagesBeginningWith(feed.route)) { + if (page.name === 'index') { + continue; + } + const entry = { + title: page.config[titleConfigPath] || page.name, + updated: page.config[updatedConfigPath] || new Date(0), + id: page.name, + url: `${feed.url}/${page.name}`, + description: page.config[descriptionConfigPath] || "", + // description: page.html?.length > 100 ? `${page.html?.substring(0, 100)}...` || "" : page.html || "", + }; + entries.push(entry); + } + + feed.entries = entries; + feed.entries.sort((a, b) => b.updated.getTime() - a.updated.getTime()); + feed.updated = new Date(0); + for (const entry of feed.entries) { + if (entry.updated > feed.updated) { + feed.updated = entry.updated; + } + } + + const atomFeed = `<feed xmlns="http://www.w3.org/2005/Atom"> +<title>${feed.title}</title> +<updated>${feed.updated.toISOString()}</updated> +<link rel="self" href="${feed.url}/atom.xml" type="application/atom+xml"/> +<link rel="alternate" href="${feed.url}" type="text/html"/> +${feed.entries + .map((entry) => { + return `<entry> +<title>${entry.title}</title> +<updated>${entry.updated.toISOString()}</updated> +<id>${entry.id}</id> +<link rel="alternate" href="${entry.url}" type="text/html"/> +<summary>${entry.description}</summary> +</entry>`; + }) + .join("\n")} +</feed>`; + + try { + const file = feed.buildPath; + const dir = path.dirname(file); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(file, atomFeed); + } catch (e) { + logger.error(`Failed to write feed ${feed.buildPath}: ${e.message}`); + return false; + } +}
\ No newline at end of file diff --git a/app/builder/pageDirectory.ts b/app/builder/pageDirectory.ts index f95bc51..b7b3d04 100644 --- a/app/builder/pageDirectory.ts +++ b/app/builder/pageDirectory.ts @@ -8,6 +8,7 @@ import matter from "gray-matter"; import { markedHighlight } from "marked-highlight"; import hljs from "highlight.js"; import hljsDefineSolidity from 'highlightjs-solidity'; +import YAML from 'yaml' hljsDefineSolidity(hljs); hljs.initHighlightingOnLoad(); @@ -47,6 +48,7 @@ export class PageDirectory { private pagesPath: string; private pages: Record<string, Page> = {}; + private feeds: Record<string, Feed> = {}; private lastFullBuild: number; constructor(pagesPath: string) { @@ -62,6 +64,10 @@ export class PageDirectory { for (const page in localPages) { await this.loadPage(localPages[page]); } + const localFeeds = glob.sync(`**/feed.yml`, { cwd: this.pagesPath }); + for (const feed in localFeeds) { + await this.loadFeed(localFeeds[feed]); + } this.lastFullBuild = Date.now(); }; @@ -97,6 +103,28 @@ export class PageDirectory { return this.pages[route]; }; + public loadFeed = async (path: string): Promise<Feed> => { + let feed; + try { + feed = YAML.parse(loadRaw(`${this.pagesPath}/${path}`)); + } catch (e) { + logger.error(`Failed to read feed ${path}: ${e.message}`); + return undefined; + } + + this.feeds[feed.pages] = { + title: feed.title, + route: feed.pages, + url: feed.url, + buildPath: `${process.env.OUTPUT_DIR}/${feed.pages}/atom.xml`, + paramStrategy: feed.paramStrategy, + updated: new Date(0), + entries: [] + } + + return this.feeds[feed.pages]; + }; + public removePage = (page: string): void => { let route = page.replace(/\.[^.]*$/, ""); delete this.pages[route]; @@ -115,6 +143,10 @@ export class PageDirectory { return Object.values(this.pages); } + public getFeeds(): Feed[] { + return Object.values(this.feeds); + } + public getPagesBeginningWith(prefix: string): Page[] { return Object.values(this.pages).filter((page) => page.route.startsWith(prefix) @@ -134,3 +166,21 @@ export type Page = { buildTime: number; config: any; }; + +export type Feed = { + route: string; + title: string; + url: string; + buildPath: string; + paramStrategy: any; + updated: Date; + entries: FeedEntry[]; +} + +export type FeedEntry = { + title: string; + updated: Date; + id: string; + url: string; + description: string; +}
\ No newline at end of file |
