-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.ts
118 lines (105 loc) · 3.64 KB
/
build.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { encodeBase64 } from "jsr:@std/encoding";
import * as lcss from "npm:lightningcss";
import { stripTags } from "jsr:@dbushell/hyperless";
const hash = (value: string): Promise<ArrayBuffer> =>
crypto.subtle.digest("SHA-1", new TextEncoder().encode(value));
const guid = async (value: string) =>
new Uint8Array(await hash(value)).reduce((a, b) => a + b.toString(36), "");
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
// Parse date string, e.g. "1st May 2024"
const parseDate = (time: string) => {
const parts = time.match(/(\d{1,2})[a-z]{2} (\w+) (\d{4})/i);
const year = Number.parseInt(parts![3]);
const month = (months.indexOf(parts![2]) + 1).toString().padStart(2, "0");
const day = Number.parseInt(parts![1]).toString().padStart(2, "0");
const date = new Date(`${year}-${month}-${day}`);
return date;
};
const template = `<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/assets/rss.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>JSX.lol</title>
<description>JSX.lol - Does anybody actually like React? A cherry-picked collection of React criticism.</description>
<link>https://jsx.lol</link>
<lastBuildDate>{{lastBuildDate}}</lastBuildDate>
<atom:link href="https://jsx.lol/rss.xml" rel="self" type="application/rss+xml"/>
<language>en</language>
{{entries}}</channel>
</rss>
`;
const entry = `<item>
<title>{{title}}</title>
<description>{{description}}</description>
<link>{{link}}</link>
<guid isPermaLink="false">{{guid}}</guid>
<pubDate>{{pubDate}}</pubDate>
</item>
`;
// Generate RSS feed from HTML
const html = await Deno.readTextFile(
new URL(import.meta.resolve("./public/index.html")).pathname,
);
let rss = template;
let lastBuildDate = "";
const entries: string[] = [];
const articles = html.match(/<article[^>]*?>(.*?)<\/article>/gs)!;
for (const article of articles) {
const heading = article.match(/<h3[^>]*?>(.*?)<\/h3>/s)![1];
const blockquote = article.match(
/<blockquote[^>]*?>(.*?)<\/blockquote>/s,
)![1];
const link = /href="([^"]*?)"/.exec(heading)![1];
const title = /<span[^>]*?>(.*?)<\/span>/.exec(heading)![1];
const description = stripTags(/<p[^>]*?>(.*?)<\/p>/.exec(blockquote)![1]);
const time = blockquote.match(/<time[^>]*?>(.*?)<\/time>/s)![1];
const id = await guid(link);
let xml = entry;
xml = xml.replace(`{{title}}`, () => title);
xml = xml.replace(`{{description}}`, () => description);
xml = xml.replace(`{{link}}`, () => link);
xml = xml.replace(`{{guid}}`, () => id);
xml = xml.replace(`{{pubDate}}`, () => parseDate(time).toUTCString());
entries.push(xml);
if (lastBuildDate === "") {
lastBuildDate = parseDate(time).toUTCString();
}
}
rss = rss.replace(`{{lastBuildDate}}`, () => lastBuildDate);
rss = rss.replace(`{{entries}}`, () => entries.join(""));
await Deno.writeTextFile(
new URL(import.meta.resolve("./public/rss.xml")).pathname,
rss,
);
const { code } = lcss.bundle({
filename: new URL(import.meta.resolve("./public/assets/main.css")).pathname,
minify: true,
sourceMap: false,
include: lcss.Features.Nesting,
});
let css = new TextDecoder().decode(code);
css = css.replace(/\/\*[\s\S]*?\*\//g, "").trim();
const cssHash = encodeBase64(
new Uint8Array(
await crypto.subtle.digest("sha-256", new TextEncoder().encode(css)),
),
);
const cssPath = new URL(import.meta.resolve("./public/assets/main.min.css"))
.pathname;
await Promise.all([
Deno.writeTextFile(cssPath, css),
Deno.writeTextFile(cssPath.replace(/\.css$/, ".txt"), cssHash),
]);