BLOG
ARTICLE
Background
Next.Js has a lot of cool features, but if there's something I find lacking is that making a sitemap for your website is not as straight as I thought it would be.
Here are my specific conditions:
- I'm using Vercel to host my website
- I have local Markdown files as my blog posts (so no fetching API)
I decided to follow the 'sitemap under pages' approach instead of creating it as static via webpack build. In my other website I used Next.Js's getInitialProps
to build xml for sitemap. So for this website I thought similar approach would work fine. This time I used getServerSideProps
. It worked in local environment via vercel dev
, but not when I deployed to Vercel.
It gave me this error:
1 2
ERROR Unhandled error during request: Error: ENOENT: no such file or directory, scandir '/var/task/posts'
In hindsight, I should have had realized what went wrong. But as I was oblivious, I could not see what the problem was. Only after I found this post, I realized what went wrong:
Vercel was complaining it could not find the directory posts
where I store my .md files.
In a javascript file that I use to read .md files as blog posts, initially I used process.cwd()
as per instruction in Next.Js blog starter.
1
const postsDirectory = path.join(process.cwd(), 'posts');
To make Vercel include posts
directory into source output during deployment, I needed to do:
1
const postsDirectory = path.resolve('./', 'posts');
So that the directory could be found!
The Solution
Here's my sitemap: https://jerfareza.me/sitemap
You can find the final solution below:
pages/sitemap.js
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
import { getAllPostsSortDescending, getTagsPaths } from 'lib/api'; const rootUrl = 'https://jerfareza.me'; const createSitemap = ( posts, tags ) => `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <static pages here> </url> ${posts.map((post) => { return ` <url> <loc>${rootUrl}/blog/${post.slug}</loc> <lastmod>${post.date}</lastmod> <changefreq>daily</changefreq> <priority>0.7</priority> </url> `; }).join('')} ${tags.map((tag) => { return ` <url> <loc>${rootUrl}${tag}</loc> <changefreq>daily</changefreq> <priority>0.5</priority> </url> `; }).join('')} </urlset> `; export async function getServerSideProps({ res }) { const posts = await getAllPostsSortDescending([ 'date', 'slug', ]); const tags = await getTagsPaths(); res.setHeader('Content-Type', 'text/xml'); res.write(createSitemap(posts, tags)); res.end(); return { props: {}, // will be passed to the page component as props }; } const Sitemap = () => { return <div></div>; }; export default Sitemap;
lib/api.js
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
import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; const postsDirectory = path.resolve('./', 'posts'); export function getSlugs() { return fs.readdirSync(postsDirectory).map(d => d.replace(/\.md$/, '')); } export async function getPostBySlug(slug, fields = []) { const file = path.join(postsDirectory, `${slug}.md`); const fileContents = fs.readFileSync(file, 'utf8'); const { data, content } = matter(fileContents); const items = {}; fields.forEach((field) => { if (field == 'slug') { items[field] = slug; } if (field == 'content') { items[field] = content.toString(); } if (data[field]) { items[field] = data[field]; } }) return items; } <content redacted>
Hi, I'm Jerfareza
Daviano 👋🏼
Hi, I'm Jerfareza Daviano 👋🏼
I'm a Full Stack Developer from Indonesia currently based in Japan.
Passionate in software development, I write my thoughts and experiments into this personal blog.