Blog reboot

Tuesday, June 18, 2019

about 6 min read

Okay, It’s been a long time since I haven’t posted something here, so here is I go.

Lately I’ve been spending some time trying Svelte.

What is Svelte?

svelte logo
svelte logo

Svelte: “Cybernetically enhanced web apps”

What do I like about it?

  • It just is so simple.
  • The learning curve is low, 3 days later I was really efficient at creating components, stores, etc.
  • It does all its things at compiler time, so the runtime is really lightweight!
  • In a previous post, I told you about this little Cooking Contest project to iterate on learning by creating something that might be useful.
  • Animations are so easy! There will be a future post about this
  • Stores are so simple to use! I will dedicate a future post about this.
  • I just 🧡 it!

This blog runs now on Sapper

You might know that at the beginning this blog was built using GatsbyJS. It was a blast!

But I wanted to test my new Svelte powers to see where I could go.

So I tried using Sapper to refactor this website.

You are currently looking at it. I will explain how I did it below.

migrated Features

TDD with Cypress

See previous article about that.

Print estimated reading time for each blog post

  • reading time : gatsby-remark-reading-time —> simple code

except for code blocks…

Write articles using Markdown

used a branch and adapted from there.

What next

I need to port those features from the previous blog:

  • better code highlighting (gatsby-remark-prismjs) with PrismJS

  • Twitter buttons (@weknow/gatsby-remark-twitter) & links (also the Twitter script should load every time)

  • gatsby-remark-a11y-emoji : add alt + aria labels for emojis in markdown content.

  • handling offline with gatsby-plugin-offline —> see service-worker.js

Favicons

Gatsby has a plugin for that too.

I resorted to generate all favicons beforehand. I created a Favicons.svelte component to include all those links in the<head> part using <svelte:head>.

Look at commit 952b83a6cf2022c34846b5685538c510e0a1e14f.

PWA Manifest

Gatsby has a plugin to automatically generate this.

Here, again, it is made beforehand. It uses the Favicons.

SEO, analytics

I already wrote an article about that.

RSS Feed

Also wrote an article about that.

replacing gatsby-plugin-typography

I used Google fonts directly:

  <style>
  @import url('https://fonts.googleapis.com/css?family=Merriweather:400,400i,700,900|Montserrat:900&display=swap');
  </style>

and then changed the fonts in the global.css, like so:

font-family: 'Merriweather', serif;
font-family: 'Montserrat', sans-serif;

migrating markdown content

This Sapper markdown blog template used marked library

I had an issue with the metadata date, for which I needed to remove the double-quote aroud the string date. Also, marked extension seem to convert everything in text, even boolean values, so beware of that (published field).

adding href anchors ids + some CSS to show a linkable header.

I have written Cypress Tests for that, but you will see that they are tests showing how to configure the marked library. They have a playground/exploratory feel to them. They do not test directly the blog site, and thus, are even quicker because there is no need to run a browser to run them.

I followed the marked documentation to add a special renderer based on their sample which I adapted and provided CSS for.

Look at commit d756b50db77e7484b9213ee75bd3dc5ee797e5a0 in the commit log.

Twitter Buttons

Put this in the svelte:head part:

  <script type="text/javascript">
    window.twttr = (function(d, s, id) {
      var js, fjs = d.getElementsByTagName(s)[0],
        t = window.twttr || {};
      if (d.getElementById(id)) return t;
      js = d.createElement(s);
      js.id = id;
      js.src = "https://platform.twitter.com/widgets.js";
      fjs.parentNode.insertBefore(js, fjs);

      t._e = [];
      t.ready = function(f) {
        t._e.push(f);
      };

      return t;
    }(document, "script", "twitter-wjs"));
  </script>

Put this in the layout:


<a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-show-count="false" data-size="large">Tweet</a>

<a href="https://twitter.com/doppelganger9?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-size="large" data-show-count="false">Follow @doppelganger9</a>

At the moment, it breaks the back link with this error:

Uncaught (in promise) TypeError: Cannot read property 'removeChild' of null
    at detach (internal.mjs:122)
    at Object.destroy [as d] ([day].svelte:118)
    at destroy (internal.mjs:1132)
    at Day.$destroy (internal.mjs:1236)
    at Day.$destroy (internal.mjs:1260)
    at App.svelte:22
    at run (internal.mjs:17)
    at Array.forEach (<anonymous>)
    at run_all (internal.mjs:23)
    at check_outros (internal.mjs:595)

So I’m not adding them for now, as I have to investigate further into this error.

How to map URLs

My old blog’s URL where like this:

  • YEAR/MONTH/DAY -> article

By default, the Sapper starter exposes in the src/routes directory a subdirectory called blog which contains an index (the list og blog posts) and the blog post detail [slug].svelte.

So, how whould In reproduce my old URL structure with Sapper? Sapper simply uses folder and filenames to create routes. In order to recreate the https://lacourt.dev/YEAR/MONTH/DAY/ URL, I would simply create a 2019/06/06 directory with an index.svelte inside it, or simply 2019/06 directory with a 06.svelte in it, which is equivalent.

TODO: make a tag in a sample repo for it

Using URL placeholders

Digging into Sapper test samples, I found out the layout app test which has a src/routes/[x]/[y]/[z].svelte which makes it possible inside [z].svelte to lookup the values of the URL placeholder parameters (x, y and z). So I just applied this to my structure to create URL placeholder parameters for YEAR, MONTH and DAY: easy!

TODO: make a tag in a sample repo for this.

In the svelte template, I changed the preload function into this:

<script context="module">
  export async function preload({ params, query }) {
    // the `slug` parameter is available because
    // this file is called [day].html
    const res = await this.fetch(`2019/${params.month}/${params.day}.json`);
    const data = await res.json();

    if (res.status === 200) {
      return {
        post: data,
        year: 2019,
        month: params.month,
        day: params.day,
      };
    } else {
      this.error(res.status, data.message);
    }
  }
</script>

What seems to be missing from Sapper

Sapper Export crawling

in order to export your pages, you have to link to them from the App, because of the way the export feature works by crawling your content.

So, it explains why in this meta-post I linked to rss and sitemap.xml endpoints.

In the Sapper GitHub Issues, I found a tips that revolves around providing an invisible link and then having more links inside.

See https://github.com/sveltejs/sapper/issues/749

Look commit f108e3184b5760a394d22ea254170e3034c3eb62.

Fall 2019 update as the PR n°825 has been merged

You can use the sapper export --entry CLI option to specify multiple roots to crawl when exporting.

For example, you can have a src/routes/surprise/index.svelte that contains links to files that are not linked from the main content. You just run sapper export --entry '/ /surprise' and the surprise directory contents linked in the index.html file will be crawled and thus exported.

I found it not really practical as I did not want to have an index for my hidden content so I’m keeping a hidden link to the hidden-links file and it will generate everything.

Service Worker and JSON data

Also, the service-worker is currently not indexing all the exported .json files.

Sapper routing

Another useful stuff would be to include another page without duplicating, for example a markdown post in another directory. Currently, I have opted for duplication, but it is not fully satisfying (see routes/future, routes/[slug], routes/2019, routes/alternate-reality: they all have the same template except of course for imports with relative paths).

I resolved this by extracting the duplicated part in a shared Post.svelte component. This has reduced the duplication.

npm run dev watch

The watch part in npm run dev does not detect changes to Markdown files.