Skip to content

Commit fa93ad6

Browse files
committed
Add rss feed for articles
1 parent 2dcddb7 commit fa93ad6

File tree

7 files changed

+129
-25
lines changed

7 files changed

+129
-25
lines changed

generate/asset.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
/**
2-
* @import {PackageJson} from 'type-fest'
3-
*/
4-
51
import assert from 'node:assert/strict'
62
import fs from 'node:fs/promises'
73
import process from 'node:process'
@@ -16,10 +12,6 @@ import {read, write} from 'to-vfile'
1612
import {reporter} from 'vfile-reporter'
1713
import {VFile} from 'vfile'
1814

19-
const packageValue = await fs.readFile('package.json', 'utf8')
20-
/** @type {PackageJson} */
21-
const packageJson = JSON.parse(packageValue)
22-
2315
dotenv.config()
2416

2517
const postCssProcessor = postcss(
@@ -69,15 +61,6 @@ for (const fp of paths) {
6961
}
7062
}
7163

72-
assert(typeof packageJson.homepage === 'string')
73-
const cname = new VFile({
74-
basename: 'CNAME',
75-
dirname: 'build',
76-
value: new URL(packageJson.homepage).host + '\n'
77-
})
78-
await write(cname)
79-
console.error(reporter(cname))
80-
8164
/**
8265
* @param {VFile} file
8366
* @returns {Promise<VFile>}

generate/index.js

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* @import {Root} from 'hast'
33
* @import {DataMap} from 'vfile'
4+
* @import {Entry as FeedEntry} from 'xast-util-feed'
45
* @import {Entry as SitemapEntry} from 'xast-util-sitemap'
56
* @import {Human} from '../data/humans.js'
67
* @import {Release} from '../data/releases.js'
@@ -29,10 +30,18 @@
2930
import assert from 'node:assert/strict'
3031
import fs from 'node:fs/promises'
3132
import {glob} from 'glob'
33+
import {fromHtml} from 'hast-util-from-html'
34+
import {isElement} from 'hast-util-is-element'
35+
import {sanitize} from 'hast-util-sanitize'
36+
import {select} from 'hast-util-select'
37+
import {toHtml} from 'hast-util-to-html'
38+
import {urlAttributes} from 'html-url-attributes'
3239
import {read, write} from 'to-vfile'
40+
import {visit} from 'unist-util-visit'
3341
import {matter} from 'vfile-matter'
3442
import {reporter} from 'vfile-reporter'
3543
import {VFile} from 'vfile'
44+
import {rss} from 'xast-util-feed'
3645
import {sitemap} from 'xast-util-sitemap'
3746
import {toXml} from 'xast-util-to-xml'
3847
import yaml from 'yaml'
@@ -416,6 +425,9 @@ page(
416425

417426
/** @type {Array<SitemapEntry>} */
418427
const sitemapEntries = []
428+
/** @type {Array<VFile>} */
429+
const learnFiles = []
430+
const now = new Date()
419431

420432
for (const render of tasks) {
421433
const {tree, file} = await render()
@@ -428,14 +440,18 @@ for (const render of tasks) {
428440
const modified = matter.modified || meta.modified
429441
assert(pathname)
430442
sitemapEntries.push({url: new URL(pathname, origin).href, modified})
443+
444+
if (matter.group) {
445+
learnFiles.push(file)
446+
}
431447
}
432448

433449
await fs.writeFile(
434-
new URL('../build/sitemap.xml', import.meta.url),
435-
toXml(sitemap(sitemapEntries))
450+
new URL('../build/CNAME', import.meta.url),
451+
new URL(origin).host + '\n'
436452
)
437453

438-
console.log('✔ `/sitemap.xml`')
454+
console.error('✔ `/CNAME`')
439455

440456
await fs.writeFile(
441457
new URL('../build/robots.txt', import.meta.url),
@@ -447,7 +463,104 @@ await fs.writeFile(
447463
].join('\n')
448464
)
449465

450-
console.log('✔ `/robots.txt`')
466+
console.error('✔ `/robots.txt`')
467+
468+
await fs.writeFile(
469+
new URL('../build/sitemap.xml', import.meta.url),
470+
toXml(sitemap(sitemapEntries))
471+
)
472+
473+
console.error('✔ `/sitemap.xml`')
474+
475+
learnFiles.sort(function (a, b) {
476+
assert(a.data.matter?.published)
477+
assert(b.data.matter?.published)
478+
return (
479+
new Date(b.data.matter.published).valueOf() -
480+
new Date(a.data.matter.published).valueOf()
481+
)
482+
})
483+
484+
const newestLearnFiles = learnFiles.slice(0, 10)
485+
/** @type {Array<FeedEntry>} */
486+
const learnEntries = []
487+
488+
for (const file of newestLearnFiles) {
489+
const tree = fromHtml(file.value)
490+
const body = select('main', tree)
491+
assert(body)
492+
const fragment = sanitize(body)
493+
494+
const {matter, meta} = file.data
495+
assert(matter)
496+
assert(meta)
497+
assert(meta.pathname)
498+
const base = new URL(meta.pathname, origin)
499+
500+
visit(fragment, 'element', function (node, index, parent) {
501+
// Make URLs absolute.
502+
for (const property in node.properties) {
503+
if (
504+
Object.hasOwn(urlAttributes, property) &&
505+
isElement(node, urlAttributes[property]) &&
506+
node.properties[property] != null
507+
) {
508+
node.properties[property] = new URL(
509+
String(node.properties[property]),
510+
base
511+
).href
512+
}
513+
}
514+
515+
if (parent && typeof index === 'number') {
516+
// Drop empty spans, left from syntax highlighting.
517+
if (
518+
node.tagName === 'span' &&
519+
Object.keys(node.properties).length === 0
520+
) {
521+
parent.children.splice(index, 1, ...node.children)
522+
return index
523+
}
524+
525+
// Drop tooltips from twoslash.
526+
if (
527+
node.tagName === 'div' &&
528+
typeof node.properties.id === 'string' &&
529+
node.properties.id.startsWith('user-content-rehype-twoslash')
530+
) {
531+
parent.children.splice(index, 1)
532+
return index
533+
}
534+
}
535+
})
536+
537+
learnEntries.push({
538+
author: matter.author,
539+
descriptionHtml: toHtml(fragment),
540+
description: matter.description,
541+
modified: matter.modified,
542+
published: matter.published,
543+
title: matter.title,
544+
url: base.href
545+
})
546+
}
547+
548+
await fs.writeFile(
549+
new URL('../build/rss.xml', import.meta.url),
550+
toXml(
551+
rss(
552+
{
553+
feedUrl: new URL('rss.xml', origin).href,
554+
lang: 'en',
555+
title: 'unified - learn',
556+
url: origin
557+
},
558+
learnEntries
559+
)
560+
) + '\n'
561+
)
562+
563+
console.error('✔ `/rss.xml`')
451564

452565
/**
453566
*

generate/molecule/footer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function footer() {
1515
h('li', {}, h('a', {href: '/community/'}, 'Community'))
1616
]),
1717
h('ol.row.justify-end-l', [
18+
h('li', {}, h('a', {href: '/rss.xml'}, 'RSS Feed')),
1819
h(
1920
'li',
2021
h('a', {href: 'https://opencollective.com/unified'}, 'OpenCollective')

generate/pipeline/main.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export const main = unified()
2626
.use(rehypeDocument, {
2727
js: ['/browser.js', '/search.js'],
2828
link: [
29+
{
30+
href: '/rss.xml',
31+
rel: 'alternate',
32+
title: 'unifiedjs.com',
33+
type: 'application/rss+xml'
34+
},
2935
// We take images from these two, so preconnect asap.
3036
{href: 'https://github.com', rel: 'preconnect'},
3137
{href: 'https://images.opencollective.com', rel: 'preconnect'},

generate/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface UnifiedjsGenerateFields {
2121
/**
2222
* Group of post; data specific to `unifiedjs.github.io`.
2323
*/
24-
group?: string | undefined
24+
group?: 'recipe' | 'guide' | undefined
2525
}
2626

2727
declare module 'vfile' {

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
"flexsearch": "^0.7.0",
3030
"glob": "^11.0.0",
3131
"hast-util-find-and-replace": "^5.0.0",
32+
"hast-util-from-html": "^2.0.0",
3233
"hast-util-heading-rank": "^3.0.0",
3334
"hast-util-is-element": "^3.0.0",
3435
"hast-util-sanitize": "^5.0.0",
36+
"hast-util-select": "^6.0.0",
3537
"hast-util-shift-heading": "^4.0.0",
3638
"hast-util-to-dom": "^4.0.0",
39+
"hast-util-to-html": "^9.0.0",
3740
"hastscript": "^9.0.0",
3841
"hosted-git-info": "^7.0.0",
3942
"html-url-attributes": "^3.0.0",
@@ -108,6 +111,7 @@
108111
"vfile-matter": "^5.0.0",
109112
"vfile-rename": "^3.0.0",
110113
"vfile-reporter": "^8.0.0",
114+
"xast-util-feed": "^2.0.0",
111115
"xast-util-sitemap": "^2.0.0",
112116
"xast-util-to-xml": "^4.0.0",
113117
"xastscript": "^4.0.0",

todo.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)