Be careful with npm shrinkwrap

Recently we had the issue that we used version x of an npm package. However in the course of time this package was updated by the author and contained a critical bug, which broke our AWS deployments. Locally this was no issue because we had a version installed that satisfied the version requirements.

In order to prevent issues like this we looked at npm shrinkwrap. This writes a file called npm-shrinkwrap.json which ‘freezes’ all of the package-versions that are installed in current project.

Now this is dangerous, as we just found out.

The issue is that when an author decides to delete a package from the feed. Delete you ask? Yes, delete, gone, no trace, nothing.

What’s the issue you might ask? You’ll find it next time?

Not really.

Imagine you’re using Elastic Beanstalk, which, based on certain triggers, can spawn a new instance of a server, or delete one.

Now today you release your application to your servers, and you shrinkwrap your packages.

Before you do that, you obviously clear your local npm cache (located in %appdata%\npm-cache), and your local node_modules. Then you do an npm install to verify every package is correctly installed, you do a few test runs, maybe on a local server. Then you package and send it of to AWS.

All runs well, you’re happy, and your boss is happy.

Next week, for whatever reason, you get a high load on your servers. Elastic Beanstalk decides to add one more instance.

And then stuff starts to break. You get emails that its health is degraded. Then you get emails that its health severe.

At 2a.m. you open your laptop, and you start looking at the logs. There you find something in the lines of:

  npm ERR! Linux 3.14.48-33.39.amzn1.x86_64
  npm ERR! argv "/usr/bin/iojs" "/usr/bin/npm" "install"
  npm ERR! node v2.4.0
  npm ERR! npm  v2.13.0
  
  npm ERR! version not found: node-uuid@1.4.4
  npm ERR! 
  npm ERR! If you need help, you may report this error at:
  npm ERR!     <https://github.com/npm/npm/issues>
  
  npm ERR! Please include the following file with any support request:
  npm ERR!     /app/npm-debug.log

What? You tested locally? What happened?

Okay, you fire up a console window. You make a test dir. You run npm installnode-uuid@1.4.4.

All goes well. Or does it?

Let’s look at the output:

C:\__SOURCES>mkdir test

C:\__SOURCES>cd test

C:\__SOURCES\test>npm install node-uuid@1.4.4
npm http GET https://registry.npmjs.org/node-uuid/1.4.4
npm http 404 https://registry.npmjs.org/node-uuid/1.4.4
node-uuid@1.4.4 node_modules\node-uuid

Notice the 404? I didn’t… But it’s important!

Now here’s what happened: locally I had node-uuid@1.4.4 in my cache, so he took that one, even though the package disappeared from the registry.

However: my new instance on Elastic Beanstalk didn’t. That’s why it failed.

So, solutions:

  • Be careful when you shrinkwrap. Stuff might break in the future, as authors delete packages
  • Create a private feed, that you curate
  • As a package author, don’t delete packages. Just don’t. Other people might depend on you.

When frameworks try to be smart, AngularJS & Expressions

One of my colleagues just discovered this bug/feature in AngularJS. Using an ngIf on a string "no" will result in false.

HTML:

<div ng-app>
    <div ng-controller="yesNoController">
        <div ng-if="yes">Yes is defined, will display</div>
        <div ng-if="no">No is defined, but will not display on Angular 1.2.1</div>
        <div ng-if="notDefined">Not defined, will not display</div>
     </div>
</div>

JavaScript:

function yesNoController($scope) {
    $scope.yes = "yes";
    $scope.no = "no";
    $scope.notDefined = undefined;
}

Will print:

Yes is defined, will display

Let’s read the documentation on expression, to see where this case is covered.

.

.

.

.

Can you find it?

Neither can I.

Fix?

Use the double bang:

        // ...
        <div ng-if="!!no">No is defined, but we need to add a double bang for it to parse correctly</div>
        // ...

JSFiddle can be found here.

For those who care, it’s not a JS thing:

// execute this line in a console
alert("no" ? "no evaluated as true" : "no evaluated as false"); // will alert "no evaluated as true"

jQuery submit button and button named submit

Suppose you have a button named "submit":

<form method="post" action="" id="myForm">
    <fieldset>
        <input type="text" name="myField" />
        <input type="submit" name="submit" value="submit form" />
    </fieldset>
</form>

And you want to submit this form with jQuery:

<script type="text/javascript">
    SomeFunction()
    {
        $("#myForm").submit();
    }
</script>

That won’t work since you have a button named ‘submit’ which then screws up the jQuery function. If you rename it to ‘submitForm’ or something it will work fine.