

Why use an npm module anyway?

Would you like to reinvent the wheels of a car or just drive the darn thing? Exactly.

The ginormous npm registry hosts hundreds of thousands ready-to-go modules you can drop into your app. From left-padding to decent date handling, and from e-mail address validation to Amazon's AWS SDK. It's all available on npm wating for you to "npm install" them.

It's also where all NativeScript plugins, and even NativeScript itself are distributed.

Sounds good, I'll install all those modules!

Woah! slow down soldier. While NativeScript works with JavaScript (either transpiled from TypeScript or used directly) and npm modules are written in JavaScript as well, you can't simply install any module and expect it to work.

Originally npm modules were designed to work inside a dedicated runtime environment: Node. This means modules sometimes expect to be running inside the Node runtime and use built-in Node modules to for instance access the filesystem or perform cryptographical functions.

What about browsers? They use npm modules, right

Excellent question. They sure do. And this is where polyfills (or shims) come in. You may have heard of Browserify for instance - (among other things) it looks for a browser declaration in an npm module's package.json and swaps out anything in main and dependencies that's also in browser. Let me show you an example. This is the relevant part of package.json of aws-sdk:

browserify bits of AWS SDK package.json

The module author shipped browser-friendly versions of their JavaScript code with the plugin and Browserify will simply swap the default implementation with the browser implementation when building for the browser.

Furthermore, many built-in modules that ship with Node have been polyfilled with browser versions as well, so Browserify is able to "fix" the vast majority of npm modules out there and allows them tro run inside a browser.

So.. let's apply what we've learned here!

A (partial) solution

What Browserify does for a browser, we can do for the NativeScript runtime. However, we don't want to impose a tool like Browserify or Webpack on you and your project (and we need a bit more flexibility as well), so the nativescript-nodeify plugin was made as a special purpose tool to swap out built-in Node modules from anything in your dependency tree by NativeScript compatible implementations.

Limitations

As much as it's true NativeScript is not a Node runtime, it's also true NativeScript is not a Browser. So not all browser polyfills may work inside the NativeScript runtime. And that's why we need to take it a step further than blindly swapping in browser-compatible modules. For instance, stream-http, a popular polyfill for Node's native http module, uses global.location.protocol.search which is not a 'thing' in NativeScript.

That's where NativeScript-specific polyfills are needed. Like this one for randombytes, this one for xml2js, or this one for fs.

The nativescript-nodeify plugin knows which polyfills work best with NativeScript, but like the stream-http example above there will always be edge cases where a little more work is required.

How do I use the plugin?

You can install dependencies as normal, and this plugin as well, then at build time a hook installed by this plugin will scan and modify your modules as it sees fit to make them {N}-compatible.

As an example, look what happens when you install node-uuid by running npm install node-uuid --save. This module uses Node's built-in crypto library on this line, which doesn't work in a browser nor NativeScript. So let's require the module in our app and see what happens at runtime:

node-uuid-without-nodeify.js

Crash! Sad!

Let's install the plugin by running tns plugin add nativescript-nodeify and run the exact same code again:

node-uuid-with-nodeify.js

No crash! Better still, you get that random UUID you longed for all your life!

Conclusion

If you encounter an npm module that's not immediately NativeScript compatible, add the nativescript-nodeify plugin and rebuild your app.

If there's still a problem at runtime open an issue on GitHub to see if we can figure it out together and enhance NativeScript's npm support even further!