diff --git a/package-lock.json b/package-lock.json
index 0198730..12d60b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1932,6 +1932,14 @@
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
+ "feed": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.0.tgz",
+ "integrity": "sha512-nLU4Fn+5TCJ1Zu9kBDqXPxsaTXaL/hZgZ3pmT87TUzS1kfaL91iIKJ+DFWygL8CrOeYw80z7QWxabkMV/x+g2g==",
+ "requires": {
+ "xml-js": "^1.6.11"
+ }
+ },
"file-type": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz",
@@ -4037,6 +4045,14 @@
"xtend": "^4.0.0"
}
},
+ "xml-js": {
+ "version": "1.6.11",
+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+ "requires": {
+ "sax": "^1.2.4"
+ }
+ },
"xml-parse-from-string": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
diff --git a/package.json b/package.json
index 1eafae2..1ca920b 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"classnames": "^2.2.6",
"compression": "^1.7.4",
"date-fns": "^2.11.1",
+ "feed": "^4.2.0",
"front-matter": "^3.1.0",
"marked": "^0.8.2",
"polka": "^0.5.2",
diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte
index 4f4d67b..a4a2c26 100644
--- a/src/components/Footer.svelte
+++ b/src/components/Footer.svelte
@@ -5,6 +5,8 @@
import twitchLogo from '../svg/iconfinder_twitch_306173.svg'
import instagramLogo from '../svg/iconfinder_38-instagram_1161953.svg'
import emailIcon from '../svg/iconfinder_mail_5474819.svg'
+ import rssIcon from '../svg/iconfinder_icon-social-rss_211914.svg'
+ import jsonFeedIcon from '../svg/json_feed_icon.svg'
export let latestPosts
@@ -65,7 +67,7 @@
.twitter :global(svg path) {
/* fill: rgb(29, 161, 242); */
- stroke: #fff;
+ stroke: #eae9be;
stroke-width: 2px;
fill: #eae9be;
}
@@ -85,6 +87,14 @@
fill: #eae9be;
}
+ .rss :global(svg) {
+ fill: #eae9be;
+ }
+
+ .json-feed :global(svg) {
+ fill: #eae9be;
+ }
+
:global(svg) {
height: 1em;
width: 1em;
@@ -110,6 +120,15 @@
color: #a7a574;
}
+ .subscribe {
+ font-weight: bold;
+ }
+
+ hr {
+ color: #86856f;
+ margin: 0.2em 0;
+ }
+
@media only screen and (min-width: 900px) {
.site-footer {
font-size: 0.8em;
@@ -182,6 +201,13 @@
{/each}
+
+
Contact
diff --git a/src/routes/blog/_content.js b/src/routes/blog/_content.js
new file mode 100644
index 0000000..ea9f0b8
--- /dev/null
+++ b/src/routes/blog/_content.js
@@ -0,0 +1,55 @@
+import { readdir, readFile } from 'fs'
+import { promisify } from 'util'
+import { basename } from 'path'
+import { pipe, partial, prop, sortBy, reverse, filter } from 'ramda'
+import fm from 'front-matter'
+import marked from 'marked'
+
+const { NODE_ENV } = process.env
+
+export async function getBlogListing(tag) {
+ const files = await promisify(readdir)(`_posts/blog/`, 'utf-8')
+ const filteredFiles = filterDevelopmentFiles(files)
+
+ const contents = await Promise.all(
+ filteredFiles.map(async file => {
+ const fileContent = await promisify(readFile)(
+ `_posts/blog/${file}`,
+ 'utf-8'
+ )
+ const parsedAttributes = fm(fileContent)
+
+ const lineOfTextRegExp = /^(?:\w|\[).+/gm
+ const lines = parsedAttributes.body
+ .match(lineOfTextRegExp)
+ .slice(0, 2)
+ .join('\n')
+
+ const preview = marked(lines)
+ return {
+ ...parsedAttributes.attributes,
+ preview,
+ slug: basename(file, '.md'),
+ }
+ })
+ )
+ const filteredContents = pipe(
+ sortBy(prop('date')),
+ reverse,
+ filter(article => article.published),
+ partial(filterByTag, [tag])
+ )(contents)
+
+ return filteredContents
+}
+
+function filterDevelopmentFiles(files) {
+ return NODE_ENV !== 'production'
+ ? files
+ : files.filter(file => !file.startsWith('dev-'))
+}
+
+function filterByTag(tag, contents) {
+ return tag ? contents.filter(content => content.tags.includes(tag)) : contents
+}
+
diff --git a/src/routes/blog/index.json.js b/src/routes/blog/index.json.js
index 671b639..770d759 100644
--- a/src/routes/blog/index.json.js
+++ b/src/routes/blog/index.json.js
@@ -1,63 +1,10 @@
-import { readdir, readFile } from 'fs'
-import { promisify } from 'util'
-import { basename } from 'path'
-import { pipe, partial, prop, sortBy, reverse, filter } from 'ramda'
-import fm from 'front-matter'
-import marked from 'marked'
-
-const { NODE_ENV } = process.env
+import { getBlogListing } from './_content'
export async function get(req, res) {
const { tag } = req.query
- const files = await promisify(readdir)(`_posts/blog/`, 'utf-8')
- const filteredFiles = filterDevelopmentFiles(files)
-
- const contents = await Promise.all(
- filteredFiles.map(async file => {
- const fileContent = await promisify(readFile)(
- `_posts/blog/${file}`,
- 'utf-8'
- )
- const parsedAttributes = fm(fileContent)
-
- const lineOfTextRegExp = /^(?:\w|\[).+/gm
- const sentenceRegExp = /(?:\w|\[).[.?!]/
- const lines = parsedAttributes.body
- .match(lineOfTextRegExp)
- .slice(0, 2)
- .join('\n')
-
- const preview = marked(lines)
- return {
- ...parsedAttributes.attributes,
- preview,
- slug: basename(file, '.md'),
- }
- })
- )
- const filteredContents = pipe(
- sortBy(prop('date')),
- reverse,
- filter(article => article.published),
- partial(filterByTag, [tag])
- )(contents)
-
+ const filteredContents = await getBlogListing(tag)
res.writeHead(200, {
'Content-Type': 'application/json',
})
res.end(JSON.stringify(filteredContents))
}
-
-function filterDevelopmentFiles(files) {
- return NODE_ENV !== 'production'
- ? files
- : files.filter(file => !file.startsWith('dev-'))
-}
-
-function filterByTag(tag, contents) {
- return tag ? contents.filter(content => content.tags.includes(tag)) : contents
-}
-
-function filterPublished(article) {
- return article.published
-}
diff --git a/src/routes/feed/_feed.js b/src/routes/feed/_feed.js
new file mode 100644
index 0000000..0629aa4
--- /dev/null
+++ b/src/routes/feed/_feed.js
@@ -0,0 +1,39 @@
+import { Feed } from 'feed'
+import { getBlogListing } from '../blog/_content'
+
+export async function getFeed() {
+ const feed = new Feed({
+ title: 'michalvanko.dev latest posts',
+ id: 'https://michalvanko.dev',
+ link: 'https://michalvanko.dev',
+ description: 'Latest posts published on michalvanko.dev',
+ copyright: 'All rights reserved 2020, Michal Vanko',
+ generator: 'sapper with Feed for node.js',
+ updated: new Date(),
+ image: 'https://michalvanko.dev/eye.png',
+ favicon: 'https://michalvanko.dev/m-favicon-192x192.png',
+ language: 'en',
+ author: {
+ name: 'Michal Vanko',
+ email: 'michalvankosk@gmail.com',
+ link: 'https://michalvanko.dev',
+ },
+ feedLinks: {
+ json: 'https://michalvanko.dev/feed.json',
+ rss: 'https://michalvanko.dev/feed.xml',
+ },
+ })
+
+ const blogListing = await getBlogListing()
+ blogListing.forEach(post => {
+ feed.addItem({
+ title: post.title,
+ id: `https://michalvanko.dev/blog/${post.slug}`,
+ link: `https://michalvanko.dev/blog/${post.slug}`,
+ description: post.preview,
+ date: post.date,
+ image: post.thumbnail ? `https://michalvanko.dev/${post.thumbnail}` : undefined,
+ })
+ })
+ return feed
+}
diff --git a/src/routes/feed/index.json.js b/src/routes/feed/index.json.js
new file mode 100644
index 0000000..ee6edd9
--- /dev/null
+++ b/src/routes/feed/index.json.js
@@ -0,0 +1,11 @@
+import { getFeed } from './_feed'
+
+export async function get(req, res) {
+ const feed = await getFeed()
+
+ res.writeHead(200, {
+ 'Content-Type': 'application/json',
+ })
+ res.end(feed.json1())
+}
+
diff --git a/src/routes/feed/index.xml.js b/src/routes/feed/index.xml.js
new file mode 100644
index 0000000..d2cb394
--- /dev/null
+++ b/src/routes/feed/index.xml.js
@@ -0,0 +1,11 @@
+import { getFeed } from './_feed'
+
+export async function get(req, res) {
+ const feed = await getFeed()
+
+ res.writeHead(200, {
+ 'Content-Type': 'application/xml',
+ })
+ res.end(feed.rss2())
+}
+
diff --git a/src/routes/index.svelte b/src/routes/index.svelte
index 67a21e5..078ba81 100644
--- a/src/routes/index.svelte
+++ b/src/routes/index.svelte
@@ -33,7 +33,11 @@
- michalvanko.dev index page
+ Introduction @michalvankodev
+
+
+
+