Thursday, December 14, 2017

Using webfonts-generator with POI

The problem

This might come as a surprise to you (it did to me at least) that POI is not handling generation of webfonts by default. It does so many wonderful things but webfonts generation is not one of them, unfortunately. You might ask what are webfonts and why do we need to generate one in a frontend project? Aren't there enough fonts to choose from already? To answer that we need to understand how browsers work when downloading multiple resources. First of all there is a limit to how many connections can be made at the same time to one server. Secondly there is a limit to how many connections can there be in total at any given time. So if, for example, you'd like to load 30 small black and white images (for use as icons) then the browser would have a lot of work to do and the page would load very, very slowly. To mitigate that problem you could use a custom font that will contain all the images, compressed, and it will take a fraction of the time to load. This is exactly what webfonts-generator does.

The solution

So how do we use it in a POI project if it is not available by default? It's actually not that simple. We need to extend the already impressive webpack configuration to teach it what to do with the font descriptors. Font descriptors are just JavaScript modules ending with .font.js exporting definition of the font, like the one below:

module.exports = {
  fontName: 'icons',
  files: [
    'phone.svg',
  ]
}

Having that in place we can start extending our configuration. As you already know POI contains webpack rules for all the common types of files. Those rules differ from production to test to development configuration. The rule we need to add for it to work properly needs to be just like the one for CSSes, just with the webfonts-loader added at the end of the use list. So first we need to find the css rule:

const isCssRule = rule => rule.test.toString().indexOf('\.css$') >= 0
const findCssRule = rules => rules.find(isCssRule)

Having that rule in hand let's create another rule for webfonts that contains the definition of a loader:

const constructWebfontRuleFromCssRule = rule => ({
  test: /\.font\.js$/,
  use: [
    ...rule.use, 
    {
      loader: 'webfonts-loader', options: {
        fileName: 'assets/fonts/[fontname].[hash:8].[ext]'
      }
    },
  ]
})

Then we can add the rule to webpack configuration in poi.config.js:

module.exports = {
  webpack (config) {
    const rule = findCssRule(config.module.rules)
    config.module.rules.push(constructWebfontRuleFromCssRule(rule))

    return config
  }
}

And finally we need to install the additional loader like so:

npm install --save-dev webfonts-loader

webfonts-generator is a transitive dependency so it will be installed automatically too.

Final thoughts

I know that doesn't seem like much but it took me way too long to figure it out so I figured I'll post it for posterity. In the meanwhile I have created a ticket in the POI project for it to be implemented as part of the default configuration so that we won't have to deal with it ourselves. Will see if that will catch on or not.

Happy building!

Thursday, December 7, 2017

POI and naming classes in CSS Modules

This will be a quick one. If you're trying to use CSS modules with POI in a Vue.js app and you want to exercise some control over what the names of the generated classes are then there is a configuration option in POI for that. It's a little bit hidden so it took mi like 5 minutes to figure it out - hence the post.

module.exports = {
  vue: {
    cssModules: {
      localIdentName: '[hash:base64:5]'
    }
  }
}

That weirdness stems from the fact that POI configures the css-loader indirectly via the vue when setting up the vue-loader. We can take advantage of the fact that it uses the Object.assign method and sneak in our configuration option.

Happy coding!

Saturday, November 25, 2017

Vue.js - the jQuery killer

Vue.js is awesome. It really is. There is however this notion that frameworks such as Angular or React are primarily for full-fledged, single-page frontend applications and that notion carries over to Vue. In this installment I am going to try to share with you a way to treat Vue as a better jQuery and to have more than a few micro-apps on one page.

The jQuery phenomena

There are more than a few reasons why jQuery became so popular. One of them was the need to target multiple browsers with one, coherent codebase. I think that this is exactly why there are so many jQuery developers out there and so few people (by comparison) that can actually use JavaScript and the DOM API. Fortunately enough, the browser wars led to standards being formed and implemented by most major browsers and now things like searching for elements in the DOM tree or toggling a class is not a challenge anymore. Let's face it: even Ajax (the biggest example of fallout from browser wars) is standardized these days! And I am not talking here about the XMLHttpRequest class but about the fetch API which makes jQuery pretty much useless.

Welcome to the 21st century

So where do frameworks like React, Angular or Vue fit in this new, jQuery-free world? Is there even a place for them? Well, as it turns out 2 things have changed over the past decade: not only browsers became more standardized but also the applications became way more demanding. It's not just a few plugins bashed together like it used to be back in the days. Now we see browsers as an operating system / development platform of its own that can target multiple hardware and deliver experiences (not "pages") wherever we go. That's the price to pay for being cool :)

For that reason the size of a codebase that use jQuery alone grows out of proportion trying to synchronize the state of multiple deeply nested components. This is not what jQuery was designed for and it's also where the scaling capabilities of it end. We need something that will match the new requirements. And so along came a long list of failures in the industry trying to solve the misery of big apps. At first it looked like the MVC pattern (so popular in the backend world) would be a good fit. I think the main reason was the overwhelming presence of design patterns in the late 2000s. And MVC was (along with Singleton) among the most recognized ones. Even good frameworks like ExtJS went to the dark side and implemented the MVC pattern. Such a shame!!!

That didn't solve the scaling problem. Apps were growing out of proportion of what was feasible at that time. It wasn't until about 2013 when Facebook's engineer Jordan Walke came up with this idea of shedding MVC in the frontend all together and replacing the ever growing connections between UI and data with mathematical precision and unidirectional data flow that we all now take for granted.

The framework problem

As it turns out the problem with frameworks is that they take control from you and let you react only when something happens. This is mostly great but sometimes it means global state needs to be maintained to gain access to the underlying data. React does away with this by embedding the state directly in components. This was, in my opinion, the biggest step forward after the definition of unidirectional data flow. However, with React came JSX (to make our life easier) and that came with Babel pre-processor or TypeScript and other goodness like webpack, gulp, grunt to be the main driver for the build process. In short: the build process became mandatory. Something you have never had to talk about when doing just jQuery. To make matter even more interesting now that we had the app it was usually so heavy (take angular 2 hello app built with the daemonic spin-off form Ember CLI) that having more than one app was virtually technically impossible.

To even approach the level of reusability jQuery provides we would need to get into the realm of tens or hundreds of apps on one page! That feels right down insane but if you come to think of it, it really opens up possibilities where previously only jQuery would fit.

The Vue

Such is the case with Vue.js. It is a small library (not necessarily a framework) that doesn't require any build steps, runs directly from the browser but if required it can grow to gigantic proportions by the share virtue of composition of components. Yes, that is right, if used with the component mindset it will easily grow beyond what was possible with jQuery (or plain old JavaScript for that matter). But it can do small bits as well! And it does it with such a grace it is absolutely stunning!

Let's see the simplest app that would run in the browser:

    new Vue({
      el: '#app',
      render: h => h({ template: '<h1>Hello, world!</h1>' })
    })

Assuming there is an element with id '#app' the app will replace that element with an H1 with the text Hello, world!. The el element doesn't need to be a CSS selector (if it would be it needs to point to one and only one element or the rest will be discarded). It can, however, be a DOM object, like so:

    new Vue({
      el: document.querySelector('#app'),
      render: h => h({ template: '<h1>Hello, world!</h1>' })
    })

Now assuming we would like to instantiate this application in every container with the class app applied we would need to do something that resembles this:

    [ ...document.querySelectorAll('.app') ].forEach(el => {
      new Vue({
        el,
        render: h => h({ template: '<h1>Hello, world!</h1>' })
      })
    })

Easy, right? Because the querySelectorApp returns an array-like object that doesn't provide the forEach method we're spreading that list over an array literal effectively creating a proper array from the list of found elements. Next, we apply the Vue application on each of the found placeholders. This is very much what jQuery was doing to us for all these years! Applying a transformation to all selected elements! Sure, it's true jQuery was less invasive (if there is anything present in the <div class="app">...</dic> Vue will go and nuke that content with its own content). But is it not like we would like to have less and less HTML managed by the backend (preferably just a placeholder for what is an app that is fully managed by the browser?

Of course the other question on the horizon is passing on information to those small apps. There is a few ways to do it (Ajax being one of them) that can help you here. My favorite is for the backend to use either a JavaScript object somewhere on the page that can be easily matched with the container. Other example (especially useful if there are just a few parameters you need to pass in) is to use the data-* attributes. Just don't go overboard with it!

Post mortem

I think that jQuery is one of the few frontend utilities that profoundly changed the way we think about software development. However the time is, finally, almost up because of the requirements new Internet applications. There is an obvious need for tools that solve problems that are not yet natively solved in the browsers in such a way that would be appropriate for the broad Internet developers audience. React and Vue are fitting nicely in that niche and do a fantastic job in being lean, focused. That gives the opportunity to do a lot more with Vue than was previously possible and to use those new tools in ways that would previously not even cross one's mind.

Happy coding

Wednesday, November 22, 2017

Writing unit tests with Vuejs and Poi

Poi is great. It's fantastic! It's one of the tools that were very much missing from the landscape. Now that we already have it let's explore what we can do with it. Let's get serious!

This time we're going to create a POI-based project that will support both Vue and unit testing.

First we need to have a project. A simple npm init will do. When asked to give the test command write poi test and you're done.

$ npm init
...
test command: poi test
...

Is this ok? (yes)

That was easy. Now let's install the required dependencies (there are quite a few of them but we'll go through the list and I will explain what each one does):

$ npm install --save-dev poi poi-preset-karma \
    webpack karma mocha chai karma-webpack karma-mocha karma-chai \
    vue vue-test-utils

poi is our build system so it is obvious it needs to be installed. poi-preset-karma is a preset for poi for it to know what to do when you execute poi test. It will use karma to run the tests.

webpack and karma-webpack allow to use webpack to bundle all the files

mocha is the testing framework giving you BDD-like tests with describe and it. karma-mocha is a package that allows easy integration of Karma and Mocha.

chai is a fantastic library for writing expectations. Similarly to Mocha, karma-chai makes it easy to integrate Karma with Chai.

And last but not least we top it with some Vue sauce. vue is our beloved frontend framework and vue-test-utils is a library that aids testing.

Let's write some tests

The default folder structure assumes unit tests to be located in test/unit folder and the files to be named like something.test.js. So let's create one:

test/unit/example.test.js

it('will pass', () => {
  expect(1).to.equal(1)
})

Easy, right? For Poi to know what to do when we run poi test we need to use a preset. To do that we'll create a poi.config.js file like so:

module.exports = {
  presets: [
    require('poi-preset-karma')({
      frameworks: [ 'mocha', 'chai' ]
    })
  ]
}

As you can see there we use Chai asserts. For Mocha to know about Chai we need to tell the Karma preset that both Mocha and Chai are to be used.

Now all that remains is to run the tests. There are 2 options for running:

  • Run just once and finish
  • Run continuously in the background and re-run tests when files change

To just run the tests and finish you issue the command

$ npm test

This works because test is a top-level npm command.

To run tests continuously with automatic re-running do this:

$ npm test -- --watch

Let's decipher the bits. npm test is pretty much obvious at this point. The double-dash is a way to tell npm hey, when you run the command specified in the scripts section then add all the parameters that follow to the execution. In our case the --watch parameter enables watching changes on files and automatic re-running of all the tests.

Poi and continuous integration server

Running tests in a real browser is great because they will be testing components in their natural habitat. It poses however a difficult problem when running them on a headless continuous integration server. This is a common problem and to address that Poi has a switch that uses ChromeHeadless instead of the regular Chrome:

$ npm test -- --headless

Of course you can combine them and run headless tests in watch mode on a remote machine via SSH using Vim or Emacs as your text editor - especially if your primary work computer is running Windows and you want things to go fast(er). The possibilities are endless.

Debugging

By default tests are being executed on Google Chrome. The page you'll see contains a little DEBUG button. When you click it a new tab will open where you can set breakpoints and examine your code. One thing to note is that by default there is a limit to how long one unit test can take (2s) and you will most likely exceed that limit if you pause in a test method. Don't be alarmed by that - just re-run your tests without breakpoints and you'll see if it works or not.

Please note that the tests in debug mode won't re-run by themselves when you change things in the sources. You need to refresh the page to start a new test run. This is done so that hot-module reloading doesn't kick in in the middle of your debug session. Very convenient!

Let's go Vueing

The time has come to make a real test that uses Vuejs. So let's create one in test/unit/components/Clicker.js that will check if upon clicking on the component a custom event will be emitted:

import Vue from 'vue'
import { mount } from 'vue-test-utils'

import Clicker from '@/components/Clicker.vue'

describe('Component: Clicker', () => {
  it('will emit "clicked" event when interacted with', () => {
    // given
    const wrapper = mount(Clicker)

    // when
    wrapper.vm.$el.click()

    // then
    expect(wrapper.emitted().clicked).to.deep.equal([ [ 'Here!' ] ])
  })
})

Now in the root folder let's create src/components and inside that let's create our component as a single-file component Clicker.vue:

<template>
  <h1 @click="$emit('clicked', 'Here!')">Hello!</h1>
</tempalte>

That's it! It is really that simple!

Post scriptum

This barely scratches the surface of what is possible with Poi but at the same time it does cover a lot of configuration that you don't need to write yourself. Just looking at the starter template for webpack-simple shows how much is being done behind the scenes.

Happy testing!

Tuesday, November 14, 2017

Making xdg-open use Chrome instead of Firefox

One of the more annoying things in Linux Mint that I came across is the fanatic persistence of using Firefox to open links after making Chrome the default browser. I know, I know... Google spying, I'm giving up privacy to some corpo that will do evil things with my sexual preferences, yadda, yadda, yadda - I don't care. I want my links to open in Chrome. Period.

Recently I came across a post on askubuntu.com that explains what needs to happen. Basically all URL opening mechanisms are using xdg-open underneath the covers so all it takes is to teach it the lesson it needs:

$ xdg-mime default google-chrome.desktop text/html
$ xdg-mime default google-chrome.desktop x-scheme-handler/http
$ xdg-mime default google-chrome.desktop x-scheme-handler/https
$ xdg-mime default google-chrome.desktop x-scheme-handler/about

Done. Now ctrl-click any links in the console and you'll have that opened in Chrome.

Happy clicking!

Saturday, October 28, 2017

Poi that!

You might already be familiar with Webpack. It's a great tool and a bastard when it comes to configuration. It's a beast! It's a daemon! It's one of those sons of bitches that you can't loose and that will hunt you down everywhere you go. Like a woman! Just look at the amount of configuration code that the vue-cli produces when used in combination with the webpack template. That is right down INSANE!!!

Although there's not much that can be done with woman, Webpack has received a shell that is finally making some sense! That shell is called POI and is created by a gentleman who goes by the name of Egoist. Let's take a closer look at what it does.

At first glance it is a simplified build tool. One that you would probably never look upon knowing what kind of power Webpack brings to the table. But if you look a bit closer it turns out it is just (easier said than done) a box around webpack configuring it to some standards using a set of predefined presets for the most common tasks. However, knowing what kind of a daemon sits in the guts of it, you'll be pleased to know that poking around that dragon is very much possible and easy at the same time.

So what does it look like to use it? First you need to know that Poi stems from the ashes of a slimming treatment of vue-cli. This means it will out-of-the-fricken-box work just perfect with Vue.js! All you need is a file that will bootstrap your application. Let's see the content of an index.js that does it.

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

And then the single-file-component App.vue

<template>
  <h1>Hello, world!</h1>
</template>

You can obviously put everything else in there (the <script> and <style> sections for example).

Now let's see how do we actually use Poi with that nice application. First of all you need to install Poi. For the sake of this oversimplified and butt-ugly example we'll perform the worst voodoo magic ever and install Poi globally. But please, beyond demonstration purposes NEVER EVER EVER EVER DO THAT. EVER. Poi is your build system so it must be part of your project. You have been warned!

npm install -g poi

Now having index.js and App.vue in that same directory run the following command:

$ poi index.js

# some useless output here

                               Asset       Size  Chunks                    Chunk Names
abf8058814c81f7a160b.hot-update.json   44 bytes          [emitted]         
                           vendor.js     1.5 MB       0             [big]  vendor
                           client.js    13.6 kB       1                    client
                         manifest.js    31.2 kB       2  [emitted]         manifest
ff42074b3ef0f4f41971.hot-update.json   35 bytes          [emitted]         
                          index.html  578 bytes          [emitted]         

> Open http://localhost:4000
> On Your Network: http://127.0.0.1:4000

 DONE  Build 664748 finished in 141 ms!

That is it! Nothing more, nothing less! You have access to everything that you would expected: Hot module reloading, bundling app in separate files so that the part that actually changes is not the whole package, an index.html that is automatically generated for you to host your application - you name it!

There is much more to poi than just running the project. For example to be able to run unit tests you need to start using poi presets. That's not very hard - requires a few more packages though.

$ npm install poi-preset-karma karma-webpack karma-mocha karma-chai webpack mocha chai

With those packages installed we need to make Poi aware how to run tests. We do that by creating a poi.config.js like so:

module.exports = {
  presets: [
    require('poi-preset-karma')({
      frameworks: [ 'mocha', 'chai' ]
    })
  ]
}

That's really it! Nothing more! Just basically 4 lines telling that we would like to use karma with mocha and chai. Jeez! That is truly magnificent! Totally Vue.js-style! Now let's have the simplest test possible. All unit tests are to be stored in test/unit folder. This location can obviously be further configured but for now it will do just fine. So let's create test/unit/example.test.js like so:

import { expect } from 'chai'

it('will work', () => {
  expect(1).to.equal(1)
})

Having that in place let's run the tests!

$ poi test

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 526 ms!

28 10 2017 13:22:30.310:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:22:30.310:INFO [launcher]: Launching browser Chrome with unlimited concurrency
28 10 2017 13:22:30.314:INFO [launcher]: Starting browser Chrome
28 10 2017 13:22:30.892:INFO [Chrome 62.0.3202 (Linux 0.0.0)]: Connected on socket BUV....
  ✔ will work

Finished in 0.002 secs / 0 secs @ 13:22:31 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed

Now ain't that a pretty! I myself was looking to simplify the use of webpack for quite some time and it is only my bad luck that I didn't run into poi sooner. I would have saved a whole lot of time! Tumbling down the rabbit hole if you would like to run those tests in headless mode you can specify the browser to use. I like headless Chrome:

$ poi test --browsers ChromeHeadless

Boooom! Your tests can now be executed in headless environments like CI! That is so easy and soooo exciting!

Want to do some code coverage?

$ poi test --coverage

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 508 ms!

28 10 2017 13:46:33.489:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:46:33.489:INFO [launcher]: Launching browser ChromeHeadless with unlimited concurrency
28 10 2017 13:46:33.493:INFO [launcher]: Starting browser ChromeHeadless
28 10 2017 13:46:33.747:INFO [HeadlessChrome 0.0.0 (Linux 0.0.0)]: Connected on socket 9Xjcc...
  ✔ will work

Finished in 0.006 secs / 0.001 secs @ 13:46:33 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed
----------|----------|----------|----------|----------|----------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
----------|----------|----------|----------|----------|----------------|
All files |      100 |      100 |      100 |      100 |                |
----------|----------|----------|----------|----------|----------------|

So one last thing that we need is to deliver our super duper app to some hosting. scp-ing files is not the task for Poi but building those files is and Poi does it very well:

$ poi build index.js

You will end up with the dist folder containing everything. One property of the created files is that they use absolute paths for resources like scripts and styles. It's great when it comes to hosting but trying it out using the file:// protocol (simply opening the index.html file in the browser) won't work. But it is very easy to fix. In poi.config.js add the webpack method like so:

module.exports = {
  // ...
  webpack(config) {
    config.output.publicPath = ''
    return config
  }
}

Now run the build again and open the resulting index.html file in the browser. Boom! It works!

There is way more to Poi than that. Check out the official documentation at http://poi.js.org and the GitHub page at http://github.com/egoist/poi. You will be amazed what's in store!

Happy... poi'ing!

Sunday, September 10, 2017

DotNet core and vuejs - a quick recipe

The problem

So you're stuck in .NET world (maybe with Sitecore or some other God forsaken creation) and you need to do some front-end code. How the hell do you get started?

The solution

Use dotnet core 2.0.0. Scaffold your application, install all the required dependencies and start the application in development mode

    $ sudo apt-get install dotnet-sdk-2.0.0
    $ dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
    $ dotnet new vue -n hello-world
    $ cd hello-world
    $ dotnet restore
    $ npm install
    $ ASPNETCORE_ENVIRONMENT="Development" dotnet run

Happy coding