Sunday, January 21, 2018

Creating your own ESLint shareable configuration

This is going to be a quick one: How to share ESLint configuration between projects.

Step 1: create a separate project

By separate I mean really separate with its own package.json and own repository.

$ mkdir eslint-config-config-name
cd eslint-config-config-name
npm init
...
About to write to .../package.json:

{
  "name": "eslint-config-config-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

Step 2: create your configuration

Since the main in package.json points to index.js this will be the spot where we put our shareable configuration. Let's start small - you can always extend it further later on as you work your way through the available rues:

module.exports = {
  rules: {
    'no-var': 'error',
  }
}

To make sure you don't forget about installing ESLint in your target project add the following lines to the package.json of your configuration project:

  "peerDependencies": {
    "eslint": ">= 4"
  }

This will ensure that when the target project doesn't have ESLint as a dependency then an error will be thrown telling that it needs to be present.

Step 3: publish your repository

In order for the configuration to be available for everyone you need to have it somewhere everyone can access it. In that case it is recommended to add the following keywords to your package.json.

  "keywords": [
    "eslint",
    "eslintconfig"
  ]

If you choose to publish it to NPM then that's fine, but honestly those config files can get pretty specific for your company therefore I would suggest just tagging and pushing to your Git repository, like so:

$ git init
git add .
git commit -a -m "Initial import"
git tag 1.0.0
git remote add origin url-to-your-repo
git push --set-upstream origin master

Make sure your repository is accessible via HTTP or HTTPS - that's going to be needed for the next step, which is...

Step 4: use the config

In order to use that preset you need to add it as a dependency or devDependency to your project:

  "devDependencies": {
    ...
    "eslint-config-config-name": "git+https://url-to-your-repo#1.0.0"
  }

That way you are not specifying just the latest version but a specific version. This is done so that projects can adopt the changes to your common configuration as they get the time for it.

Next we need to make use of the configuration in our local project. We do that by creating either a local .eslintrc.js or by adding that configuration to package.json. Since we have already created an example .eslintrc.json in the previous step let's see it being used in package.json:

  "eslintConfig": {
    "extends": [
      "eslint-config-config-name"
    ]
  }

That's it! Now when you npm install your dependencies then the configuration and all its dependencies will be installed along with it.

If you would like to see a working example take a look at Vue.js + Electron example on Github that uses the common configuration I prepared for my company.

Happy linting!

Sunday, January 14, 2018

Electron - where Vue.js meets desktop

Today I was pushed by some unseen force to check out Electron - the desktop platform that embeds Chrome for everything. Let me tell you - it is amazing! Not only you have just one browser to care about but it automatically runs on all sorts of platforms! Well, I am sold on the idea - how about you? If you're like me then keep reading.

Getting started

First we need a sort of web application. For obvious reasons I am choosing Vue.js (don't even get me started on Angular X or React - I could bitch about them forever). So Vue.js it is. Obviously we need a build system for it. And what better can we get than POI! So let's do a Vue.js app.

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (vue-electron) 
version: (1.0.0) 
description: 
entry point: (index.js) main.js
test command: poi test
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to .../vue-electron/package.json:

{
  "name": "vue-electron",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "poi test"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) 

$ npm install --save-dev poi poi-class-component

Since this will be an Electron app we need the electron package too:

$ npm install --save-dev electron

Now we're all set to create our app. In the src folder let's create 2 files: index.js and App.vue

src/index.js

import App from './App.vue'

new App({ el: '#app' })

src/App.vue

<template>
  <h1>{{ message }}</h1>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class App extends Vue {
  message = "Hello, world!"
}
</script>

<style>
h1 {
  background-color: green;
}
</style>

Easy, right? Now here comes the hard part: embedding the application in Electron. I mean, it's not really hard if you know how :). There is a good documentation for both Electron and POI on how do to it but nowhere is there one place that shows the whole solution. So there you have it.

poi.config.js

module.exports = {
  entry: './src/index',
  webpack(config) {
    config.target = 'electron-renderer'
    return config
  },
  homepage: './',
}

main.js

const electron = require('electron')
const isDev = require('electron-is-dev')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({width: 800, height: 600})

  const entryDev = 'http://localhost:4000'
  const entryProd = url.format({
    pathname: path.join(__dirname, 'dist/index.html'),
    protocol: 'file:',
    slashes: true
  })
  mainWindow.loadURL(isDev ? entryDev : entryProd)

  if (isDev) mainWindow.webContents.openDevTools()

  mainWindow.on('closed', function () {
    mainWindow = null
  })
}

app.on('ready', createWindow)

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

Now we need a few things to be able to run it. First there needs to be a way of starting the web application and then the Electron container hosting our app. We'll do that by using the concurrently module and by adding a start script to our package.json. While we're at it let's also install electron-is-dev package used in the main.js script:

$ npm install --save-dev concurrently
$ npm install --save electron-is-dev

Since the electron-is-dev package will be part of our Electron app and will not be bundled with the rest of the app then it needs to be added as a regular, transitive, dependency.

Now the promised start script. Remember, that goes into the "scripts" section in your package.json

"start": "concurrently \"poi\" \"sleep 10 && electron .\""

Please note the sleep 10 before starting electron. That is to give the initial bundler run enough time to start serving our app.

And that is it! Your Vue.js app runs in Electron! And yes, it does hot reloading! Isn't that cool? Well... How about giving your app to other people? You wouldn't do that this way as you do in development, would you? We need some bundling techniques. Luckily for us there is a package called electron-packager that has our backs covered.

Bundling

Let's start by installing the electron-packager module:

$ npm install --save-dev electron-packager

With that covered let's add a few more scripts:

"build": "npm run clean && npm run compile && npm run package",
"clean": "rm -rf dist $npm_package_name-*",
"compile": "poi build --no-clear",
"package": "electron-packager . --ignore=\"src|poi.config.js|.gitignore\""

I think they are pretty self explanatory. Now from the console when you start the npm run build command it will first remove all leftovers, then build the client application using POI and then package the application for your platform. If you would like to additionally package your app for a different operating system then you can always start the package target with additional parameters, like so:

$ npm run package -- --platform=win32 --arch=all

And that is it! Electron is super cool - go check it out! And as a reward for getting to those last words here's a link to a GitHub repository that has a ready-made working example: https://github.com/padcom/vue-electron-example. Besides the mentioned settings it also contains ESLint integration that I described in my previous post.

Happy coding!

Saturday, January 13, 2018

POI, Vue and ESLint

Writing code is hard. It is however much harder to write code that conforms to standards that are already in the project. Fortunately for us there is ESLint with all the bells and whistles that help in that tedious task. And believe it or not there are ready-made presets and plugins that make the configuration really simple.

Installation

First, as usual, we need some packages added to our project:

$ npm install --save-dev eslint poi-preset-eslint eslint-plugin-vue babel-eslint
Then create or update your existing poi.config.js with as follows:
module.exports = {
  presets: [
    require('poi-preset-eslint')({ mode: '*' }),
  ]
}

Configuration

All that is left is to configure ESLint. I prefer to have a set of rules that don't get in the way while developing but hold the forth when building final version. That is why some of my settings are environment-dependant:

// This defines the mode of operation for checks depending on the environment
const mode = process.env.NODE_ENV === 'production' ? 'error' : 'warn'

module.exports = {
  parserOptions: {
    'parser': 'babel-eslint',
    'sourceType': 'module'
  },

  extends: [ 'eslint:recommended', 'plugin:vue/recommended' ],

  rules: {
    'camelcase': [ 'error', { 'properties': 'always' } ],
    'func-name-matching': 'error',
    'func-names': [ 'error', 'never' ],
    'object-shorthand': [ 'error', 'always' ],
    'prefer-const': 'error',
    'prefer-template': 'error',
    'template-curly-spacing': [ 'error', 'never' ],
    'no-useless-rename': 'error',
    'no-useless-constructor': 'error',
    'arrow-spacing': [ 'error', {
      'before': true,
      'after': true,
    } ],
    'arrow-parens': [ 'error', 'as-needed' ],
    'arrow-body-style': [ 'error', 'as-needed' ],
    'no-const-assign': 'error',
    'prefer-numeric-literals': 'error',
    'indent': [ 'error', 2, {
      'SwitchCase': 1,
    } ],
    'semi': [ 'error', 'never' ],
    'quotes': [ 'error', 'single' ],
    'comma-dangle': [ 'error', 'always-multiline' ],
    'no-console': mode,
    'no-debugger': mode,
    'no-alert': mode,
    'no-var': 'error',
    'one-var': [ 'error', 'never' ],
    'space-before-function-paren': [ 'error', {
      'anonymous': 'never',
      'named': 'always',
      'asyncArrow': 'always',
    }],
    'object-curly-spacing': [ 'error', 'always' ],
    'array-bracket-spacing': [ 'error', 'always' ],
    'computed-property-spacing': [ 'error', 'never' ],
    'key-spacing': [ 'error', {
      'beforeColon': false,
      'afterColon': true,
    } ],
    'keyword-spacing': 'error',
    'space-infix-ops': 'error',
    'space-unary-ops': 'error',
    'space-in-parens': [ 'error', 'never' ],
    'comma-spacing': [ 'error', { 'before': false, 'after': true } ],
    'no-whitespace-before-property': 'error',
    'no-multi-spaces': 'error',
    'no-multiple-empty-lines': [ 'error', { 'max': 1 } ],
    'dot-location': [ 'error', 'property' ],
    'getter-return': 'error',
    'consistent-return': [ 'error', { 'treatUndefinedAsUnspecified': true } ],
    'valid-jsdoc': 'error',
    'eqeqeq': 'error',
    'no-return-assign': [ 'error', 'always' ],
    'vue/max-attributes-per-line': [ 'error', {
      singleline: 5,
      multiline: { max: 5, 'allowFirstLine': true },
    } ],
    'vue/html-indent': [ 'none' ],
    'vue/attribute-hyphenation': [ 'error', 'never' ],
  },
}

As for the rules (and remember this is a personal preference), I don't like the fact of being forced to write every single attribute for a component in a separate line. I think this looks awful. Sure, if there are more than X attributes then it should be in a separate line. That's why the number is so high (5). Also, I found that the hyphenation of attributes that are bound, like :selection-mode causes compilation errors so this one is (at least currently) broken.

Practice

A hint for transitioning projects from non-eslint to eslint: when you'll start with the project there will be a massive number of violations. I mean like hundrets of them. Before you commit to fixing them all consider selectively disabling ESLint in all files by means of the following directive:

/* eslint-disable */

Then, when you will get the time remove that directive from 1 file and fix warnings. Then move to the next until ESLint is enabled in all the files. That will make the job much more approachable!

And that is it! Of course you can add or change more rules as you see fit. Whatever works for you.

Happy coding!

Sunday, December 31, 2017

Creating libraries with Vue components

Vue.js is great. There is no question about it. It's small, versatile, conscious, easy to learn - you name it. With the addition of POI it is probably also one of the most friendly frameworks to work with. Let's see how we can share single-file components between projects!

Method 1: npm link

npm link is probably one of the least known features if your primary occupation is creation of web application and you have never created an NPM package yourself. It has been there in NPM for ages and is the primary tool for library creators.

To use it when you have created your library and want to test it then what you do is you run

npm link

in the library folder and you run

npm link <library-name>

in the project that you want to use the library in. That gives you practically a hard binding between the library and the project using a symlink which means that if you introduce a change in the library then the project sees those changes immediately. It's a fantastic tool when you're in the process of working on the library. It can be further streamlined when we specify a dependency using the file: protocol like so:

"dependencies": {
  "my-library": "file:path-to-library"
}

This is also a great option if the library itself is part of your project and is being used to simply tear the application code from the reusable components. This is a great idea because if the application code changed often and the components change seldom then the size of your client.js gets smaller as all code from node_modules (this includes libraries referenced with the file: protocol) get pushed into the vendor.js block.

"dependencies": {
  "project-components": "file:../project-components"
}

This will automatically setup a link named project-components inside node_modules. What's even more interesting, when you do the npm install to install dependencies then the project-component's dependencies will be installed in ../project-components/node_modules automatically thus each project that uses that library won't need a copy of the shared dependencies.

Method 2: npm publish

When you want to go for a reusable library that you'd like to share between multiple projects then the right way to go is to use a repository. There is a multitude of options but the one I find really useful in an organization are npmjs.com for all things opensource and verdaccio for all the packages that you would like to keep available only for in-house components. The nice thing about verdaccio is that it automatically acts as a proxy to npmjs.com so all you do in your project is you tell npm that the repository is located where verdaccio is

npm set registry http://localhost:4873/

and you're all set.

Example

To show you how it is done I have created a sample project on GitHub. It consists of 3 parts: the library containing 2 components, a host application using those components and a little bit of automation to start the repository server.

Patterns

When creating a component library there are 2 options that one can use:

  • include the modules that build the library
  • include the built version of the library

Which one to use depends on the library itself. If you create a library that is to be used in all sorts of projects but you wrote it in some non-standard way (let's say you used CoffeeScript) then to make it useful for the rest of us you need to point the main to the compiled version. However, if you created a library specifically for use within a specific environment it makes sense to create an index.js and point your main entry in package.json to it and to put all your code in the lib folder:

export { default as componentA } from './lib/componentA.vue'
export { default as componentB } from './lib/componentB.vue'
That way, if someone wants to include all of your library they just include it and all is great:

import myLib from 'myLib'

Or using requirejs:

const myLib = require('myLib')

If for whatever reason they want to include only a portion of it, let's say just one or two components and not the whole shebang, then they are free to do so as follows:

import componentA from 'myLib/lib/componentA.vue'

Or using requirejs:

const componentA = require('myLib/lib/componentA.vue')

The latter form allows for the treeshaking algorithm to exclude everything that is not used by the application in the resulting file thus making the final bundle smaller (sometimes a lot smaller - see the use of lodash).

To create a compiled version of your library with POI all you need is to run the following command:

poi build --library index.js

and in the dist folder you'll get a library.js that you point the main in package.json to and you're done!

That's it! It is really simple to create libraries shared between parts of your project and not all that much more complex to create universal applications shared with everyone else in the NPM ecosystem.

Happy coding in 2018!

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