feat: RSS feed
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-oxlint": "~1.60.0",
|
||||
"eslint-plugin-vue": "~10.8.0",
|
||||
"feed": "^5.2.1",
|
||||
"jiti": "^2.6.1",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"oxlint": "~1.60.0",
|
||||
@@ -622,6 +623,8 @@
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"feed": ["feed@5.2.1", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-jTynzYPWs9ALjro0GW8j7sv9y7cJBeOdD4Y88kVqYy/eyusIX3g+499JiTDIlD9Ge/unebx57T4Uzo6vpYvMtA=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
@@ -900,6 +903,8 @@
|
||||
|
||||
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.99.0", "", { "os": "win32", "cpu": "x64" }, "sha512-ipuOv1R2K4MHeuCEAZGpuUbAgma4gb0sdacyrTjJtMOy/OY9UvWfVlwErdB09KIkp4fPDpQJDJfvYN6bC8jeNg=="],
|
||||
|
||||
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||
|
||||
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
|
||||
|
||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
@@ -1006,6 +1011,8 @@
|
||||
|
||||
"wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="],
|
||||
|
||||
"xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<link rel="stylesheet" type="text/css" href="/src/style.scss" />
|
||||
<meta property="og:url" content="%VITE_BASE_URL%" />
|
||||
<meta property="og:title" content="%VITE_APP_TITLE_NO_HTML%" />
|
||||
<link rel="alternate" href="%VITE_BASE_URL%/rss" type="application/rss+xml" title="RSS 2.0" />
|
||||
<link rel="alternate" href="%VITE_BASE_URL%/atom.xml" type="application/atom+xml" title="Atom 2.0" />
|
||||
<link rel="alternate" href="%VITE_BASE_URL%/feed.json" type="application/json" title="JSON Feed 1.0"/>
|
||||
%VITE_CUSTOM_HEAD%
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-oxlint": "~1.60.0",
|
||||
"eslint-plugin-vue": "~10.8.0",
|
||||
"feed": "^5.2.1",
|
||||
"jiti": "^2.6.1",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"oxlint": "~1.60.0",
|
||||
|
||||
+52
-11
@@ -1,5 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import process from 'process'
|
||||
import { Feed } from 'feed'
|
||||
|
||||
process.loadEnvFile()
|
||||
|
||||
@@ -28,6 +29,10 @@ function readArticleMetadata(path: string): Record<string, string> | null {
|
||||
const metadata: Record<string, string> = {
|
||||
path: path.replaceAll('/index.md', ''),
|
||||
}
|
||||
const dateMatch = path.match(/(\d{4}\/\d{2}\/\d{2})/)
|
||||
if (dateMatch && dateMatch[1]) {
|
||||
metadata['date'] = dateMatch[1].replaceAll('/', '-')
|
||||
}
|
||||
do {
|
||||
subMatch = METADATA_REGEX.exec(match[1])
|
||||
if (subMatch && subMatch[1] && subMatch[2]) {
|
||||
@@ -69,6 +74,18 @@ function formatArticlePage(metadata: Record<string, string>, baseHtml: string):
|
||||
return outHtml
|
||||
}
|
||||
|
||||
function addFeedArticle(metadata: Record<string, string>, feed: Feed) {
|
||||
if (metadata.draft !== 'true') {
|
||||
feed.addItem({
|
||||
title: metadata.title.replace(/(<([^>]+)>)/gi, '').trim(),
|
||||
id: metadata.path,
|
||||
link: `${process.env.VITE_BASE_URL}${metadata.path}/`,
|
||||
date: new Date(Date.parse(metadata.date)),
|
||||
image: metadata.thumbnail.replace('./', process.env.VITE_BASE_URL + metadata.path + '/'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const indexContent = fs.readFileSync('dist/index.html', { encoding: 'utf8' })
|
||||
|
||||
if (!indexContent) {
|
||||
@@ -76,15 +93,39 @@ if (!indexContent) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
getFiles('articles')
|
||||
.filter((path) => path.match(/index.md$/))
|
||||
.forEach((path) => {
|
||||
const metadata = readArticleMetadata(path)
|
||||
if (metadata) {
|
||||
fs.writeFileSync(
|
||||
`dist/${metadata.path}/index.html`,
|
||||
formatArticlePage(metadata, indexContent),
|
||||
)
|
||||
console.info(`Wrote dist/${metadata.path}/index.html`)
|
||||
}
|
||||
const metadatas = getFiles('articles')
|
||||
.filter((path) => path.match(/\d{4}\/\d{2}\/\d{2}\/index.md$/))
|
||||
.map((path) => readArticleMetadata(path))
|
||||
.filter((metadata) => !!metadata)
|
||||
|
||||
const feed = new Feed({
|
||||
title: process.env.VITE_APP_TITLE?.replace(/(<([^>]+)>)/gi, '').trim() ?? '',
|
||||
id: process.env.VITE_BASE_URL,
|
||||
link: process.env.VITE_BASE_URL,
|
||||
language: process.env.VITE_APP_LANG,
|
||||
favicon: process.env.VITE_BASE_URL + '/articles/favicon.ico',
|
||||
generator: 'md-blog',
|
||||
feedLinks: {
|
||||
json: process.env.VITE_BASE_URL + 'feed.json',
|
||||
atom: process.env.VITE_BASE_URL + 'atom.xml',
|
||||
rss: process.env.VITE_BASE_URL + 'rss',
|
||||
},
|
||||
updated: new Date(
|
||||
Math.max(
|
||||
0,
|
||||
...metadatas
|
||||
.filter((metadata) => metadata.draft !== 'true')
|
||||
.map((metadata) => Date.parse(metadata.date)),
|
||||
),
|
||||
),
|
||||
})
|
||||
|
||||
metadatas.forEach((metadata) => {
|
||||
fs.writeFileSync(`dist/${metadata.path}/index.html`, formatArticlePage(metadata, indexContent))
|
||||
console.info(`Wrote dist/${metadata.path}/index.html`)
|
||||
addFeedArticle(metadata, feed)
|
||||
})
|
||||
|
||||
fs.writeFileSync('dist/feed.json', feed.json1())
|
||||
fs.writeFileSync('dist/atom.xml', feed.atom1())
|
||||
fs.writeFileSync('dist/rss', feed.rss2())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { REPOSITORY, NAME, VERSION } from '@/lib/meta'
|
||||
import { REPOSITORY, NAME, VERSION, BASE_URL } from '@/lib/meta'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -9,8 +9,9 @@ import { REPOSITORY, NAME, VERSION } from '@/lib/meta'
|
||||
<hr />
|
||||
<footer>
|
||||
<small>
|
||||
{{ new Date().getFullYear() }} - Made with <a :href="REPOSITORY">{{ NAME }}</a>
|
||||
{{ VERSION }}
|
||||
{{ new Date().getFullYear() }} — Made with
|
||||
<a :href="REPOSITORY">{{ NAME }} {{ VERSION }}</a> —
|
||||
<a :href="BASE_URL + 'atom.xml'"><i icon="rss"></i> RSS</a>
|
||||
</small>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
@@ -5,3 +5,4 @@ export const VERSION = packageJson.version
|
||||
export const REPOSITORY = packageJson.repository
|
||||
export const TITLE = import.meta.env.VITE_APP_TITLE
|
||||
export const SIGNATURE = import.meta.env.VITE_APP_SIGNATURE
|
||||
export const BASE_URL = import.meta.env.BASE_URL
|
||||
|
||||
Reference in New Issue
Block a user