Add Yazzy integration (#12)

* Remove eslint completely

* Make the 'last updated' time relative

* Restore accidentally commited feeds

* Add yazzy link to each article
This commit is contained in:
Carter McBride 2024-07-04 14:56:32 -06:00 committed by GitHub
parent 6b1e3157ec
commit 740191e050
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 128 additions and 122 deletions

12
biome.json Normal file
View file

@ -0,0 +1,12 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

View file

@ -122,6 +122,11 @@
<div class="article-timestamp"> <div class="article-timestamp">
<relative-time data-time="{{item.timestamp}}">{{ item.timestamp | relative}}</relative-time> <relative-time data-time="{{item.timestamp}}">{{ item.timestamp | relative}}</relative-time>
</div> </div>
{% if yazzyUrl %}
<div class="article-links">
<a href="{{ yazzyUrl }}/{{ item.link }}" target="_blank" rel="noreferrer noopener">yazzy</a>
</div>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -141,7 +146,7 @@
{% endif %} {% endif %}
<footer> <footer>
<hr> <hr>
<p>Last updated {{ now }}.</p> <p>Last updated <relative-time data-time="{{ now }}">{{ now | relative}}</relative-time>.</p>
<p> <p>
<a href="https://github.com/carterworks/rss-reader">View on GitHub</a> <a href="https://github.com/carterworks/rss-reader">View on GitHub</a>
</p> </p>

View file

@ -1,9 +0,0 @@
import pluginJs from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
export default [
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
];

View file

@ -1,46 +1,44 @@
{ {
"name": "bubo-reader", "name": "bubo-reader",
"version": "2.0.2", "version": "2.0.2",
"description": "A simple but effective feed reader (RSS, JSON)", "description": "A simple but effective feed reader (RSS, JSON)",
"homepage": "https://github.com/georgemandis/bubo-rss", "homepage": "https://github.com/georgemandis/bubo-rss",
"main": "src/index.ts", "main": "src/index.ts",
"type": "module", "type": "module",
"scripts": { "scripts": {
"clean": "rm -rf dist", "clean": "rm -rf dist",
"build": "bun src/index.ts", "build": "bun src/index.ts",
"check": "biome check --write ./{src,config,public} ./eslint.config.js" "check": "biome check --write ./{src,config,public} ./*.json --no-errors-on-unmatched"
}, },
"author": { "author": {
"name": "George Mandis", "name": "George Mandis",
"email": "george@mand.is", "email": "george@mand.is",
"url": "https://george.mand.is" "url": "https://george.mand.is"
}, },
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/georgemandis" "url": "https://github.com/sponsors/georgemandis"
}, },
"bugs": { "bugs": {
"url": "https://github.com/georgemandis/bubo-rss/issues", "url": "https://github.com/georgemandis/bubo-rss/issues",
"email": "george+bubo@mand.is" "email": "george+bubo@mand.is"
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@feelinglovelynow/get-relative-time": "^1.1.2", "@feelinglovelynow/get-relative-time": "^1.1.2",
"chalk": "^5.2.0", "chalk": "^5.2.0",
"node-fetch": "^3.3.1", "node-fetch": "^3.3.1",
"nunjucks": "^3.2.4", "nunjucks": "^3.2.4",
"rss-parser": "^3.13.0" "rss-parser": "^3.13.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.8.1", "@biomejs/biome": "^1.8.1",
"@types/bun": "latest", "@types/bun": "latest",
"@types/nunjucks": "^3.2.2", "@types/nunjucks": "^3.2.2",
"@types/xml2js": "^0.4.11", "@types/xml2js": "^0.4.11",
"tslib": "^2.5.3", "tslib": "^2.5.3",
"typescript": "^5.1.3", "typescript": "^5.1.3",
"typescript-eslint": "^7.13.1" "typescript-eslint": "^7.13.1"
}, },
"trustedDependencies": [ "trustedDependencies": ["@biomejs/biome"]
"@biomejs/biome"
]
} }

View file

@ -69,7 +69,8 @@ details:has(li.has-recent) {
} }
.article-timestamp, .article-timestamp,
.feed-url { .feed-url,
.article-links {
font-size: 0.75em; font-size: 0.75em;
} }

View file

@ -88,6 +88,7 @@ const finishBuild: () => void = async () => {
data: sortedFeeds, data: sortedFeeds,
errors: errors, errors: errors,
info: buboInfo, info: buboInfo,
yazzyUrl: process.env.YAZZY_URL,
}); });
// write the output to public/index.html // write the output to public/index.html
@ -116,48 +117,48 @@ const processFeed =
feed: string; feed: string;
startTime: number; startTime: number;
}) => }) =>
async (response: Response): Promise<void> => { async (response: Response): Promise<void> => {
const body = await parseFeed(response); const body = await parseFeed(response);
//skip to the next one if this didn't work out //skip to the next one if this didn't work out
if (!body) return; if (!body) return;
try { try {
const contents: FeedItem = ( const contents: FeedItem = (
typeof body === "string" ? await parser.parseString(body) : body typeof body === "string" ? await parser.parseString(body) : body
) as FeedItem; ) as FeedItem;
contents.feed = feed; contents.feed = feed;
contents.title = getTitle(contents); contents.title = getTitle(contents);
contents.link = getLink(contents); contents.link = getLink(contents);
// try to normalize date attribute naming // try to normalize date attribute naming
for (const item of contents.items) { for (const item of contents.items) {
item.timestamp = getTimestamp(item); item.timestamp = getTimestamp(item);
item.title = getTitle(item); item.title = getTitle(item);
item.link = getLink(item); item.link = getLink(item);
const timestamp = new Date(Number.parseInt(item.timestamp)); const timestamp = new Date(Number.parseInt(item.timestamp));
const eightHoursAgo = new Date(); const eightHoursAgo = new Date();
eightHoursAgo.setHours(eightHoursAgo.getHours() - 8); eightHoursAgo.setHours(eightHoursAgo.getHours() - 8);
item.isRecent = timestamp > eightHoursAgo; item.isRecent = timestamp > eightHoursAgo;
}
contents.hasRecent = contents.items.some((item) => item.isRecent);
contentFromAllFeeds[group].push(contents as object);
process.stdout.write(
`${success("Successfully fetched:")} ${feed} - ${benchmark(startTime)}\n`,
);
} catch (err) {
process.stdout.write(
`${error("Error processing:")} ${feed} - ${benchmark(
startTime,
)}\n${err}\n`,
);
errors.push(`Error processing: ${feed}\n\t${err}`);
} }
finishBuild(); contents.hasRecent = contents.items.some((item) => item.isRecent);
};
contentFromAllFeeds[group].push(contents as object);
process.stdout.write(
`${success("Successfully fetched:")} ${feed} - ${benchmark(startTime)}\n`,
);
} catch (err) {
process.stdout.write(
`${error("Error processing:")} ${feed} - ${benchmark(
startTime,
)}\n${err}\n`,
);
errors.push(`Error processing: ${feed}\n\t${err}`);
}
finishBuild();
};
// go through each group of feeds and process // go through each group of feeds and process
const processFeeds = () => { const processFeeds = () => {

View file

@ -10,6 +10,8 @@ import { readFile } from "node:fs/promises";
import { getRelativeTime } from "@feelinglovelynow/get-relative-time"; import { getRelativeTime } from "@feelinglovelynow/get-relative-time";
import type { Feeds, JSONValue } from "./@types/bubo"; import type { Feeds, JSONValue } from "./@types/bubo";
const yazzyUrl = process.env.YAZZY_URL;
/** /**
* Global filters for my Nunjucks templates * Global filters for my Nunjucks templates
*/ */
@ -23,7 +25,7 @@ env.addFilter("formatTime", (dateString): string => {
return !Number.isNaN(date.getTime()) ? date.toLocaleTimeString() : dateString; return !Number.isNaN(date.getTime()) ? date.toLocaleTimeString() : dateString;
}); });
env.addGlobal("now", new Date().toUTCString()); env.addGlobal("now", new Date().getTime());
// load the template // load the template
const template: string = ( const template: string = (
@ -35,15 +37,18 @@ const render = ({
data, data,
errors, errors,
info, info,
yazzyUrl,
}: { }: {
data: Feeds; data: Feeds;
errors: unknown[]; errors: unknown[];
info?: JSONValue; info?: JSONValue;
yazzyUrl?: string;
}) => { }) => {
return env.renderString(template, { return env.renderString(template, {
data, data,
errors, errors,
info, info,
yazzyUrl,
}); });
}; };

View file

@ -1,30 +1,23 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "esnext", "module": "esnext",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"removeComments": true, "removeComments": true,
"strict": true, "strict": true,
"importHelpers": true, "importHelpers": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"target": "ES2021", "target": "ES2021",
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
"sourceMap": false, "sourceMap": false,
"resolveJsonModule": true, "resolveJsonModule": true,
"outDir": "dist", "outDir": "dist",
"baseUrl": ".", "baseUrl": ".",
"typeRoots": [ "typeRoots": ["src/@types"],
"src/@types" "paths": {
], "*": ["node_modules/*", "src/@types"]
"paths": { }
"*": [ },
"node_modules/*", "include": ["src/**/*"]
"src/@types" }
]
}
},
"include": [
"src/**/*"
]
}