Hemanth.HM

A Computer Polyglot, CLI + WEB ♥'r.

URL Navigation From Service Worker

| Comments

Given that the service worker has only readonly access to the navigator.location it is not possible to change the location from within the service worker, but with WindowClient.navigate API it is easy to do a navigation!

The navigate() method of the WindowClient interface loads a specified URL into a controlled client page then returns a Promise that resolves to the existing WindowClient.

Say, you want to navigate to a particular URL (/meow.html in this case ;)) after the service worker is activated, you could something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.addEventListener('activate', event => {
  event.waitUntil(self.clients.claim().then(() => {
    /*
      returns a Promise for a list of service worker clients.
      type could be: window, worker, sharedworker or all.
    */
    return self.clients.matchAll({type: 'window'});
  }).then(clients => {
    return clients.map(client => {
      // `WindowClient.navigate()` is not yet supported in all the browser.
      if ('navigate' in client) {
        return client.navigate('meow.html');
      }
    });
  }));
});

Hope with this it's clear now on how a service worker can navigate the clients it controls to a given URL.

Happy navigating! ;)

Update: Directly from the spec:

The navigate() method must run these steps or their equivalent:

  • Let url be the result of parsing url with entry settings object's API base URL.

  • If url is failure, return a promise rejected with a TypeError.

  • If url is about:blank, return a promise rejected with a TypeError.

  • If the context object's associated service worker client's active worker is not the incumbent settings object's global object's service worker, return a promise rejected with a TypeError.

  • Let promise be a new promise.

  • Run these steps in parallel:

    • Let browsingContext be the context object's associated service worker client's global object's browsing context.

    • If browsingContext has discarded its Document, reject promise with a TypeError and abort these steps.

    • Queue a task to run the following substeps on the context object's associated service worker client's responsible event loop using the user interaction task source:

      • Navigate browsingContext to url with replacement enabled and exceptions enabled. The source browsing context must be browsingContext.

      • If the navigation throws an exception, reject promise with that exception and abort these steps.

      • If the origin is not the same as the service worker's origin, then:

        • Resolve promise with null.

        • Abort these steps.

      • Let client be the result of running Capture Window Client algorithm, or its equivalent, with browsingContext as the argument.

      • Resolve promise with client.

  • Return promise.

Webpack: Bundle Unimported Assets

| Comments

If you reading this post, I assume that you are enough awareness of webpack, so I shall be directly diving into the code part of it.

Scenario:

Assume that there are few less file that you need to bundle along with webpack's output but those are not required or imported in any of your source/target files, below is simple trick in webpack config to bundle such files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const ExtractSass = {

  entry: glob_entries('./public/styles/external/*.less'),
      output: {
          filename: './.build/junk/[name].[chunkhash].js',
      },
      module: {
          loaders: [
              {
                  test: /\.less$/,
                  loader: ExtractTextPlugin.extract('isomorphic-style-loader','css-loader?modules&localIdentName=[name]_[local]','less-loader')
              },
          ]
      },
      plugins: [
          new ExtractTextPlugin('./.build/css/[name].css')
      ]
}

The entry attribute in webpack config bascialy handles, string, array and object:

  • If a string is passed it's resolved to a module which is loaded upon startup.

  • If an array is passed all modules are loaded upon startup and the last one is exported.

  • If an object is passed multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.

The above snippet has the following depencies:

/public/styles/external/*.less has those files that are not required in any of src files but needs to bundled, the trick bascially is to create js file for each those less files and then extract them to their respective css files to ./build/css/ path.

Sample output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Child
    Hash: c1d6d83b3be5f8bb84ab
    Version: webpack 1.13.1
    Time: 3738ms

     Asset                                          Size        Chunks            Chunk Names
    ./.build/junk/Header.2fcd80b30e542a8a90e2.js    1.48 kB      1  [emitted]     Header
    ./.build/junk/Info.349ad333e96a6d35313f.js      1.47 kB      2  [emitted]     Info
    ./.build/junk/Section.a33bf0dc98964f4593b9.js   1.52 kB      4  [emitted]     Section

          ./.build/css/Header.css                   77 bytes     1  [emitted]     Header
          ./.build/css/Info.css                     96 bytes     2  [emitted]     Info
          ./.build/css/Section.css                  493 bytes    3  [emitted]     Section
        + 77 hidden modules
    Child extract-text-webpack-plugin:
            + 2 hidden modules
    Child extract-text-webpack-plugin:
            + 2 hidden modules
    Child extract-text-webpack-plugin:
            + 2 hidden modules
    Child extract-text-webpack-plugin:
            + 2 hidden modules
    Child extract-text-webpack-plugin:
            + 2 hidden modules

This might be very rare scenario or a workaround for some server side rendering stuff, none the less, it's not all that hacky and looks neat?

Path Resolver With JavaScript Proxies

| Comments

So today morning, when I was lurking on #javascript channel on freenode, there was this question:

merpnderp> If I have var foo = {x:{y:{z:bar}}}; And I have var keys = ['x','y','z']; How would I make something like foo[[keys.join('.')]] = 'baz'; work?

Me and @doodadjs were trying to solve this using [].reduce and then there was this silly thought of extending the same code to proxies

So, here is the code:

1
2
3
4
5
6
var handler = {
    get: function(target, path){
        return path.split(".").reduce((o, k) => o && (k in o) ? o[k] : undefined, target)
};

var obj = new Proxy({}, handler);

Now one could do something like:

1
obj['a'] = {b: {c : 1}}

and then obj['a.b.c'] would be equivalent to obj['a']['b']['c'] and not to worry non-existing keys would just return undefined as in

obj['a.x.c'] would return undefined.

P.S: Havn't checked the perfs yet, but this was just for the fun of it!

ServiceWorker Communication via MessageChannel

| Comments

Two way communication between ServiceWorker and main thread is easily possible with MessageChannel API.

Let the code do the talking:

In your app.js have a simple sendMessage method that looks like:

1
2
3
4
5
6
7
8
9
function sendMessage(message) {
  return new Promise((resolve, reject) => {
    const messageChannel = new MessageChannel();
    messageChannel.port1.onmessage = function(event) {
      resolve(`Received a direct message from the ServiceWorker: ${event.data}`);
    };
    navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2])
  });
}

And in your sw.js recive and respond to the message:

1
2
3
4
self.addEventListener('message', function(event) {
  console.log(`Received a message from main thread: ${event.data}`);
  event.ports[0].postMessage(`Roger that! - "${event.data}"`);
});

Now from your app.js if you want to send a message to service worker and get a reponse from it you would:

1
2
3
var log = console.log.bind(console);
var error = console.error.bind(console);
sendMessage('hello').then(log, error).catch(error);

This would log something like:

1
2
Received message from main thread: Hello
Received direct message from service worker: Roger that! - "Hello"

String.prototype.replace You Might Have Missed!

| Comments

If you ask for an example of String.prototype.replace in JavaScript, the most common response would be:

1
2
var str = 'foobar';
var replaced = str.replace('bar', 'baz');

or

1
2
var str = 'The quick brown fox had a great Xmas';
var replace = str.replace(/Xmas/i, 'Christmas!');

So it's mostly about seach and replace feature that most (P.S: By most of them I mean those I have come across on IRC.) of them wouldn talk about but would have not noticed that the signature of the replace method is like:

1
replace(regexp|substr, newSubStr|function[, flags])

From the signature, the focus of this post will be on the function in the params, this will be invoked to create the new substring and receives the below parameters:

  • match: The matched substring.

  • p1,p2..pn: The nth parenthesized submatch string.

  • offset: The offset of the matched substring.

  • string: The entire string which is being processed.

Let us see few example application of the same:

A simple example of increasing your protein and fat intake ;)

1
2
3
> function replacer(match) { return 2 * match }
> '10 gms of protein and 5gms of fat.'.replace(/[0-9]+/g, replacer)
> '20gms of protein and 10gms of fat.'

The below snippet replaces a Fahrenheit degree with its equivalent Celsius degree, for an input 212F, the function returns 100C, nothing big about this, but if you notice that the replace function's second agrument is a function convert which receives the parameters specified in the table above and returns a string.

1
2
3
4
5
6
7
8
9
10
// from mdn

function f2c(temprature) {
  function convert(match, p1, offset, string) {
    return ((p1 - 32) * 5/9) + 'C';
  }
  var s = String(temprature);
  var test = /(-?\d+(?:\.\d*)?)F\b/g;
  return s.replace(test, convert);
}

This feature would be more useful when you are doing a replace operation over a loop, using a replacer function like this one could totally avoid a loop.

Say, the input is an string that looks like:

1
let str = '___.___.___.__._...__';

Where __. is a high singal and __. is a low singal and rest of them are noise, you can filter them easily with an explicity loop using the replacer function like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let str = '___.___.___.__._...__';
let res = [];
str.replace(/(___.)|(__.)/g, function(match, p1, p2) {
  if (p1) { res.push({ high: true, length: p1.length }); }
  if (p2) { res.push({ high: false, length: 1 }); }
});

console.log(res);

/*
[ { high: true, length: 4 },
  { high: true, length: 4 },
  { high: true, length: 4 },
  { high: false, length: 1 } ]
*/

Well, it has been a decent amount of time since I wrote amount some fundamentals of JavaScript, hope this was useful for you, feel free to share few of your experince with replacer functions.

365 Days of Github

| Comments

Today my github streak looks like below, well this should have happened 210 days before if I were to have a good internet connection on my vacation in the Andamans.

I have open sourced about 474 projects so far (i.e is from 2008), some are tiny and some are decently large.

I have been part of larger of projects like yeoman koajs git-extars and other orgs as well.

I don't have the exact count of humans who have crossed the 365 days mark, but I have come across couple of dozens who have and each of them would have their own experiences, given the geo location and the projects they have worked on.

Let me start with related FAQs and few picks from my AMA and then will try to share my learning from it.

What is the graph all about?

A profile contributions graph is a record of contributions you've made to GitHub repositories.

That includes:

  • Issues and pull requests opened within the last year on a standalone repository.

  • All the commits made within the last year.

  • Contributions to Private repos are not visible to all. (Just a caution)

Can we cheat here?

Yes, a bot could help you to fake a streak but it's even more easier to find if it's faked or it's a genuine effort.

How do you get time to commit?

Well, to be very frank I am not very good at managing time, but it's more of a habit. Related AMA#21

How did it all start?

Well, I have been lurking around in Github from Jul 25, 2008, I was contributing to Ubuntu from 2005ish over launchpad, my first major contribution was for diaspora and later on yeoman

I manage my side projects by collaborating with humans! (:

Related AMA#11 and AMA#4

What do you get from this?

Nothing, but the below feeling!

Learnings:

  • Don't over burn yourself!

  • Don't get anxious it's just a streak.

  • Start as many as projects/orgs you can, collaborate with humans and contribute more!

  • It took about 180 days for this to become a part of life.

  • Make new mistakes, make them in public out and loud, that is one of the finest ways to learn.

  • Accept negative criticism with a smile.

  • Accept and work on all the positive criticism.

  • Ask people about their thoughts on IRC, Social Media et.al

  • Pause and reflect at times.

  • Make a public list of TODO items, so that people will help you with it and you will be cautious about them.

  • Make sure your projects have good readme!

  • Make meta repos for ideating if needed.

  • Invite friends to collaborate, get people started with FOSS.

  • Names of the repos matter a lot! Specially if it's a node module, ruby gems or similar.

  • Don't wait for permission of others to send a PR or start an org.

  • Don't get addicted! (Trying not to ;) )

  • Thank people who helped you, out loud.

  • Sometimes whatever you do need not be a success story, it's not meant to be as well, do it for the fun of it!

  • Pickup new stuff now and then, experiment a lot!

  • Never stop! Getting burnt in this is very common, have seen great minds getting tired at this and have stopped contributing :(

Well, this is all I can think of for now, I am sure I would have missed many points here, will update as and when I get more learning from this habit.

Are You Async Yet?

| Comments

Previsouly I had written a post on intro to ES7/ES2016 async-await in this post I shall take a simple example which shall demonstarte ways to reach to a callbackless code.

Getting a random comic with xkcd-imgs module.

var xkcd = require('xkcd-imgs'); xkcd.img(function(err, res){ if(!err) { console.log(res); } });

Same code with async-await

var promisify = require('es6-promisify'); var xkcd = promisify(require('xkcd-imgs').img); console.log(await xkcd());

Let us consider more generic example of fetching some data from an URL. (Internals of xkcd-imgs module)

var request = require('request'); request('http://xkcd-imgs.herokuapp.com/', function (error, response, body) { if (!error && response.statusCode == 200) { console.log(body); } });
var result = JSON.parse(await require("request-promise")('http://xkcd-imgs.herokuapp.com/')); console.log(result);

Or you could use the fetch API

var fetch = require('node-fetch'); await (await fetch('http://xkcd-imgs.herokuapp.com/')).json();

Indeed there babeljs to take care of async-await compilation for you!

So dive in and enjoy async programming in JavaScript without callbacks ;)

Recursive Map, Reduce and Filter in Javascript

| Comments

Here is a more functional way of map, reduce and filter functions in javascript:

map:

1
2
let map = (fn,list) => !list.length ? []:
  [fn(list[0])].concat(map(fn,list.slice(1)));
1
map(x=>x+1,[1,2,3,4)) // => [2,3,4,5]

reduce:

1
2
let reduce = (fn, value, list) => (!list.length) ? value :
  reduce(fn, fn(value,list[0]),list.slice(1));
1
reduce((x,y)=>x+y,0,[1,2,3,4]); // => 10

filter:

1
2
3
let filter = (predicate,list) => !list.length ? []:
  (predicate(list[0]) ?
    [list[0]] : []).concat(filter(predicate,list.slice(1)));
1
filter(x=>x>2,[1,2,3,4,5]); // => [3,4,5]

Do let me know if you are aware of better way to do the same ;)

Authoring Node Modules

| Comments

So far I have authored/contributed around 200+ modules on npm and they get around 1.5M downloads per month, which is a decent figure; as a part of this I have learnt a few things and I would like to share the same in this post.

Todo before writing a module:

  • Search npm and look for an already existing module that solves your issue: you could do a npm search <module> or search from the site

  • If you find one such good module, do read its code and see if you can improve something, it might be performance, logic or test fixes.

  • If you find a module and it does not do exactly what you are looking for, then send a PR with the changes you wish.

Writing a neat package:

  • If you are one of those smart lazy devs, you might want to use generator-node. If that is too heavy for you then checkout generator-nm

  • If you do not want generators you can stick with npm init and create the required dir structure manually.

  • Make sure your package.json is valid, maybe you would need package-json-validator

  • If you are new to npm you can use validate-npm-package-name to check if your package name is a valid npm package name.

  • It would be good if you make sure that your package.json has a files attribute which in turn has a main attribute in it.

  • Make sure you have test cases in place and also integrate it with continuous build service, like travis.

  • Use badges like: nodei, david et.al

  • Make sure your README.md looks good, as it has info about how to install your module, what it does, API docs and a gif, logo is a plus.

  • Take care while releasing package, using tools like release-it

Getting to the top:

There are many ways to showcase your module so that people will find it and hopefully use it but here are a few good ways to create a useful package:

  • Dig into GitHub and find a few trending projects, read the source, find code that can be extracted into a module, make a module and send a PR with it, most of time it gets accepted as modularity always wins.

  • Dig into the most dependent modules of node and repeat the above steps.

  • If the above did not work in your favour, make your own light weight module that just does a small part of the larger module.

P.S: It's not just about getting to the top, rather it's about doing one thing and doing it the best way.

Further reading:

P.S: Thanks to @nihar108 for typo and grammar fixes.

ES7 Async Await

| Comments

async/await of ES7 is one my fav proposals, that would for sure change the way we handle asynchronous code in javascript!

Let see it's application with few examples:

Promise sample:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('Promises 1 '); }, 30);
  });
}

let p2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('Promise 2'); }, 10);
  });
}

async function getVal() {
  var res1 = await p1();
  var res2 = await p2();
  return res1 + res2;
}

(async function() {
  var res = await getVal();
  console.log(res); // Promise 1 Promise 2
}());

FS stuff:

1
2
3
4
5
var lieFS = require('lie-fs'); // Promisifed FS.
(async function() {
  var list = await fs.readdir('~/');
  console.log(list);
}());

Chain animation [From the spec]:

1
2
3
4
5
6
7
8
9
async function chainAnimationsAsync(elem, animations) {
  var ret = null;
  try {
    for (let anim in animations) {
      ret = await anim(elem);
    }
  } catch(e) { /* ignore and keep going */ }
  return ret;
}

async await fetch!

1
let data = await (await fetch(url)).json()

Do let me know if you have your own set, until then happy async/await!