sharpoblunto

Over the event horizon, toward the singularity

Unit testing React components in libraries using npm link

Posted on Jan 25, 2016 react node

Lets say you have a library of common react components, and that you’re using this library in some other project. If you have some unit tests in your project which use these library components, things work just fine when your project has installed your component library via npm install - but lets say you want to use a local development copy of your components library and npm link it to your parent project so you can make changes to the library easily. Now when you run your parent projects unit tests, you’ll probably see something nasty like this

Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.

The reason for this is that your components library, and your parent project both have thier own locally installed dependency on react, and when you load the unit tests your library components are using the copy of react from the component library, and your parent project is using its own copy of React. React doesn’t like it when you load multiple instances of it at once, and you’ll see the above error when you do. Usually when installed via npm install, this isn’t a problem as the components library probably declares react as a peerDependency, and would rely on the parent projects copy - but when using a development copy using npm link the component library probably has a copy of react installed locally as a devDependency, leading to duplicate copies of React and the problem described above.

After struggling with this issue, I found a workaround that doesn’t involve changing the library or parent project considerably, so I thought I’d post it here to help anyone who has this issue in future. Include this file before you require or import React and the error will magically go away. The easiest way I’ve found to include this is to have a single file index.js which exports all your libraries components, and in your package.json add the file below as loader.js and set it as the “main” file. This file should then, after patching the loader, require index.js. This way you don’t have to change any of your component code.

const m = require('module');
const originalLoad = m._load;

const packageConfig = require('../package.json');
const peerDeps = packageConfig.peerDependencies;

/** 
 when other users of this library write tests that use components from this library
 we need to ensure that the users copy of peer dependancies are used, and not this
 libraries own local copy (React component tests will fail as it doesn't like it 
 when multiple copies are loaded at once). To fix this we'll do some patching of 
 the Node module loader
*/
if (peerDeps) {
  m._load = function(request,parent,isMain) {
    if (peerDeps[request]) {
      const parents = [];
      while (parent) {
        parents.push(parent);
        parent = parent.parent;
      }
      // reverse the usual node module resolution. Instead
      // of trying to load a local copy of the module and 
      // going up until we find one, we will try to resolve 
      // from the top down, this way peerDeps are preferentially 
      // loaded from the parent instead.
      parent = parents.pop();
      while (parent)
      {
        try 
        {
          return originalLoad(request,parent,isMain);
        }
        catch (ex) {
          parent = parents.pop();
        }
      }
    } else {
        return originalLoad(request,parent,isMain);
    }
  }
}
//Now export the library components
module.exports = require('./index');
m._load = originalLoad;
NOTE:

One caveat to note is that if you are building a distribution of your components library using webpack, you’ll have to conditionally exclude this code from running by using a conditional and the webpack DefinePlugin otherwise webpack builds will fail due to not being able to load the ‘module’ module.

Funemployment project one - Pixel art Hong Kong

Posted on Mar 5, 2015 pixelart

Through a complicated and not particularly interesting sequence of events I’ve ended up with a few weeks of (f)unemployment in between my last incredible journey and my next job. Rather than let this opportunity go to waste I’m planning on filling my time with a few projects I’ve been meaning to get around to doing.

Heres project #1 - my attempt at a pixel art rendering of Hong Kong.

Hong Kong in pixels

Carlos the Jekyll

Posted on Nov 20, 2014 programming

I recently decided to shift a bunch of my websites (sharpoblunto.com, junkship.net, matchstickframework.org) over from my ancient home built asp.net based framework to something a bit more reliable and low maintenance. I didn’t really want to put a huge amount of effort in to port the sites over, so that cut out options that would involve learning whole new languages and frameworks. Also after looking over what my requirements were it pretty quickly became apparent that I didn’t really require any dynamic functionality that couldn’t be covered via client side javascript. Because of this it seemed like a static site generator would be the way to go - I already had the front-end html templates from the existing sites, I would just have to adapt them to whatever I chose.

After a quick look around Jekyll emerged as the most suitable solution, helped in part by the fact that one can host Jekyll powered sites for free on GitHub using a custom domain name. Porting over the asp.net MVC view files to Jekyll templates was pretty straightforward (If your curious how it all looks, this sites source is available here on GitHub)

The most time consuming job was transferring the content of my old blog archive database (where all the blog content was stored as html) and converting it to markdown that could be rendered by Jekyll. The first step was to write a bit of c# to dump each database post row into a file. Then I needed to convert the html to markdown; to do this I wrote a Node.js script based on the to-markdown package to convert the html archive which I’ve included below.

var toMarkdown = require('to-markdown').toMarkdown;
var fs = require('fs');

function processFile(file) {
    fs.readFile(file,"utf8",function(err,data) {
        var md = toMarkdown(data);

        fs.writeFile(file,md,function(err) {
            console.log('processed '+file);
        });
    });
}

fs.readdir('.',function(err,files) {
    for (var i = 0;i < files.length; ++i) {
        if (files[i].indexOf(".markdown") > 0)
            processFile(files[i]);
    }
});

Its weird how technology sometimes circles back on itself, some of the very first websites I ever built were static html, and now in 2014 I’ve ditched server side code and databases in favour of static html again. I guess it goes to show that sometimes the simplest solutions are often the best.

Getting a tolerable windows command line

Posted on Apr 1, 2014 ramblings programming

After being spoiled with the power of the command line and all the great text based tools on OSX and Linux it can be hard when one comes back to windows to be presented with the default windows cmd prompt in its full archaic glory. The good news though is that with a bit of messing around it is possible to get a halfway decent command line experience on windows. Below is a screenshot of my current command line setup which looks and behaves very similarly to my iterm2 setup on OSX. If you follow the instructions below, this too can be yours.

image

First install Clink (http://mridgers.github.io/clink/). Clink offers bash style rich completion and history and integrates directly into the default windows cmd prompt. This goes a long way toward making the navigation experience easier.

Next Install Conemu (http://code.google.com/p/conemu-maximus5/). Conemu provides tabbed/split pane console windows with customizable color schemes and layouts. In my case I use the solarized theme and choose to hide the default tab bar and status bar for a minimalist view. I also add a little transparency to the windows to help see the contents of windows underneath the command prompt.

Now to get some decent command line tools to use in our shiny new terminal interface. To do this we will Install the chocolatey package manager (http://chocolatey.org/ – Powered by Nuget, its basically apt for windows). Run the following powershell command to install chocolatey

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin

Now we’re going to install some packages containing some windows ports of some useful Unix tools. Firstly we never want to use notepad.exe again so lets install vim instead. Run the following command to install vim using chocolatey

cinst vim

In order to be able to use the standard Unix command line nomenclature such as ‘ls’ instead of ‘dir’ we are going to have to install some Cygwin packages. To do this we need to install some more chocolatey packages

cinst cyg-get
cyg-get default
cinst wget
cinst curl

You can also install openssh if you don’t want to use PUTTY for ssh into your OSX/Linux machines

cyg-get openssh

Feel free to customize things further to your hearts content. This setup works for me, but I’m sure there’s a multitude of tweaks and tools out there which could make it even better.

The Dukebox of Hazzard, open source at last

Posted on Jul 1, 2013 dotnet dukes programming

The Dukebox of Hazzard (Dukes) is a media player built around the idea of fair and equal access – in particular those situations where there is only one set of speakers in a communal space, and multiple people who want to be able to play their music. Dukes is designed to prevent people cutting other peoples tracks off partway through (by design there’s no way to skip tracks), and ensures that all users get an equal chance to hear their music by running through a round-robin of all the users who have submitted tracks for playing.

I built Dukes about 2 years ago so my colleagues and I could play music in the office without getting into arguments, and it proved to work really well. We still got into terrible arguments over whatever nu-metal/80’s power ballad/drone doom tracks some people were into, but Dukes ensured that no one had the power to shut anyone’s picks off the air.

Dukes consists of a C# application that indexes media on the host machine as well as running a standalone HTTP server that runs the players web UI. All the indexed music metadata is stored in a small SQLite database, the music player uses the FMOD audio library, and the web UI communicates with the Dukes server using a set of JSON API’s.

The only problems with Dukes were that the UI was pretty terrible, and due to the fact that it was a tool used in-house primarily be software developers, it was very much left in a it works on my machine state. I always intended to open source the app, but really wanted to file off some of those rough edges before I did so. So this weekend I did just that, I ripped out the old web UI and replaced it with a shiny minimalist UI based on Bootstrap along with some help from Handlebars.js.

image

I also ripped through the server side c# code and gave everything a good polish, which consisted of fixing a bunch of incorrect concurrency handling, and removing a pile of half baked unnecessary features. I also wrote an NSIS installer script so that people can now easily install Dukes on their own machines.

image

You can get the source for Dukes here on GitHub, or if you want you can download the installer from my projects page.

You might also have noticed that this site looks a little different as well – In addition to polishing up Dukes, I also went Bootstrap crazy and redid the skin for this site. The big plus from doing this is that the site now looks pretty nice on mobile devices without having to go through all the pain and suffering of having to build properly responsive CSS from scratch. Its definitely a simpler, and cleaner design than I had before, and hopefully its not too generically bootstrap’ish looking.

Latest tweet

News Archive