Tobias Watzl

Programmer, photographer, engineer.

Using the Icarus Hexo Theme without CDNs

In this post I describe how the Icarus theme for Hexo can be used without CDNs.

Tobias Watzl

6-Minute Read

So in my first posts I promised you that I will remove the dependency on external CDNs. While the deployment process of my blog still not works flawless due to some bogus JavaScript errors I managed to get at least rid of the CDNs.

Why?

External CDNs are handy, however they pose a security risk to the users. The external hoster can change the script without my knowledge and suddenly different, in the worst case malicious code is served to my users. There might be a small benefit for loading speed and size to the user, because the users browser might have the libraries cached from a previous visit of another website that included the same script, but I do not think this justifies the risk. Additionally script blockers like uMatrix block scripts from external sources per default.

Regarding the loading speed: I try to keep the number of dependencies as small as possible and try to serve as much static content as possible, so loading should be fast anyway.

Another issue is the thing which is called GDPR. While it is probably not an issue I do not want to make my users load scripts from somewhere else. Also there has been a court ruling from the ECJ a few days ago which states that a web page owner is responsible if the Facebook Like Button transmits user data. You can read more about in this news article (German).

I will also have to pay attention to this issue, if I will embed Twitter or YouTube in the future.

How?

My blog setup is based on Hexo. Hexo itself provides the basis and renders all the markdown documents etc. and a theme, which has to be installed additionally, defines all the layout and stuff.

In my case I use the Icarus theme for Hexo. This theme uses CDNs by default. Hexo itself does not use any CDNs. In order to serve the scripts from my server instead of loading them from a CDN there are two steps necessary:

  1. make sure that the server serves the scripts
  2. also do this for fontawesome
  3. replace the CDN URLs in the HTML files with references to my server
  4. and finally replace the google webfonts

Step 1

The first step I managed thanks to the help of YoshinoriN who answered my question on GitHub.

So I installed webpack first using the following npm command:

npm install webpack webpack-cli copy-webpack-plugin --save-dev

then I installed all the dependencies using npm

npm i --save bulma jquery justifiedGallery lightgallery mathjax moment outdatedbrowser pace-js

I created the following webpack config

const CopyPlugin = require('copy-webpack-plugin');
const path = require('path');

module.exports = {
  plugins: [
    new CopyPlugin([
      { from: './node_modules/bulma/css/bulma.min.css', to: './libs/css/bulma.css' },
      { from: './node_modules/lightgallery/dist/', to: './libs/dist/' },
      { from: './node_modules/justifiedGallery/dist/css/', to: './libs/dist/css/' },
      { from: './node_modules/justifiedGallery/dist/js/', to: './libs/dist/js/' },
      { from: './node_modules/jquery/dist/', to: './libs/dist/' },
      { from: './node_modules/outdatedbrowser/outdatedbrowser/', to: './libs/outdatedbrowser/' },
      { from: './node_modules/moment/min/', to: './libs/min/' },
      { from: './node_modules/mathjax/unpacked/', to: './libs/unpacked/' },
      { from: './node_modules/pace-js/pace.min.js', to: './libs/pace.min.js' },
      { from: './node_modules/clipboard/dist/clipboard.min.js', to: './libs/dist/clipboard.min.js' },
      { from: './node_modules/highlight.js/styles/', to: './libs/styles/' },
    ])
  ],
  output: {
    path: path.resolve(__dirname, './public/')
  }
};

as well as an empty JavaScript file at `./src/index.js' which serves just as an entrypoint for webpack. I don’t know if there is a better way, but I could not find anything about that.

What do you mean this looks like an ugly hack to you? Well that’s maybe because it is, but it seems there is no better way at the moment. As far as I know webpack was designed to build your fancy webapp with js, ts, sass and whatnot, however since the generate part is already done by hexo we don’t really need any of that. The reason why we create such a messy folder structure at the target folder is that Icarus expects it that way, because it would be the same if you would fetch the stuff from jsdelivr.

Step 2

Why do I handle fontawesome separately? Because it annoyed me the most. It is not the only package that annoyed me, for example justified gallery has two packages available on npm one is justifiedGallery (correct one) and another one is justifiedGallery (wrong one).

But fontawesome pushed this to the next level. Many tutorials on the web show that you should install it using npm install font-awesome however that only installs the old version 4.7.0.

To install the new version 5 you need to run npm install @fortawesome/fontawesome-free, but of course there is no notice at the page for the old npm package or at the repo that is linked there. You have to debug it yourself in your browser and find out that the class fas does not exist in version 4.7.0.

Did you catch the second pitfall already? No?

It is @foRtawesome not @foNtawesome. Why, just why? Not just are those names only different in one letter (Hamming Distance = 1), but lowercase ‘n’ and ‘r’ look very similar. At least in the monospace font that Fedora lxqt uses and to be honest who is looking that closely. Who thinks of things like this and why?

After you installed everything you need to add the following two lines to the webpack.config.js:

{ from: './node_modules/@fortawesome/fontawesome-free/css/', to: './libs/fontawesome/css/' },
{ from: './node_modules/@fortawesome/fontawesome-free/webfonts/', to: './libs/fontawesome/webfonts/' },

Step 3

Now that we made sure our server delivers the scripts and stylesheets needed, we can configure icarus to fetch the stuff from our own server instead of the CDNs. This is done by replacing the following lines in the _config.yml file of the Icarus theme:

# CDN provider settings
# https://ppoffice.github.io/hexo-theme-icarus/Configuration/Theme/speed-up-your-site-with-custom-cdn/
providers:
    # Name or URL of the JavaScript and/or stylesheet CDN provider
    cdn: jsdelivr
    # Name or URL of the webfont CDN provider
    fontcdn: google #/${ type }?family=${ fontname }'
    # Name or URL of the webfont Icon CDN provider
    iconcdn: fontawesome

with

# CDN provider settings
# https://ppoffice.github.io/hexo-theme-icarus/Configuration/Theme/speed-up-your-site-with-custom-cdn/
providers:
    # Name or URL of the JavaScript and/or stylesheet CDN provider
    cdn: /libs/${filename} 
    # Name or URL of the webfont CDN provider
    fontcdn: google #/${ type }?family=${ fontname }'
    # Name or URL of the webfont Icon CDN provider
    iconcdn: /libs/fontawesome/css/all.min.css

Also in order to run you have to add the following snippet to your package.json file

  "scripts": {
    "generate": "webpack --mode production && hexo generate",
    "deploy": "webpack --mode production && hexo generate --deploy"
  }

and then you can generate using

npm run generate

and deploy using

npm run deploy

Step 4

As you noticed I still have google as provider for the webfonts. No I did not forget. However as of writing this blog post I could still not figure out a way to locally host them. Apparently there is a google-fonts-plugin for webpack, but it does nothing. I also tried using the mini-css-extract-plugin, but without success and downloading 300MB of fonts is not worth it for me. After all it is only stylesheets and fonts. WCGW :D

Maybe I manage to tackle that in the future, but for now I just want to provide you with blog posts. Also I should still fix the includes for the ‘cookieconsent’ Hexo plugin.

Recent posts

Categories

About

Blog