Roemer
Roemer

Reputation: 3576

Build package for both node and browser environment

I have developed a node NPM package which is mainly a wrapper (using node's http, https and querystring modules) for a specific JSON API. It is build in Coffeescript and enables a Node.js server to communicate with this API. The Api is mainly REST.

Now I want this library to be also available for the browsers. This means the calls to the http modules needs to be replaced with XMLHttpRequest (asynchronous). It seems to me I would make a wrapper for the adapter. For the Node environment, this adapter would pass all the calls to the http module, and for the browser environment to the XMLHttpRequest object.

Is there a nice way to make a build system so that the npm package contains both version, and I can publish the plain "browser-version" also on Github? The node package then is available via require('package-name') and should place a JS file (for the browser) in a directory.

I have looked in Component, which is nice for client-side package managing, but the problem remains how to create different build environments.

Upvotes: 13

Views: 4137

Answers (4)

hannad rehman
hannad rehman

Reputation: 4341

add this at the end of the module. and export your module like this.

if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  module.exports = YourModule;
}
else {
  if (typeof define === 'function' && define.amd) {
    define([], function() {
      return YourModule;
    });
  }
  else {
    window['YourModule'] = YourModule;
  }
}

Upvotes: 1

Tom Söderlund
Tom Söderlund

Reputation: 4747

You could use node-browser-resolve which adds a browser section to package.json:

{
  "browser": {
    "./index.js": "./browser.js"
  }
}

See https://nolanlawson.com/2017/01/09/how-to-write-a-javascript-package-for-both-node-and-the-browser/

Upvotes: 1

Amitay Dobo
Amitay Dobo

Reputation: 1388

An example solution for cross-developing for node.js and browsers using browserify: https://github.com/amitayd/grunt-browserify-jasmine-node-example (and discussion at my blog post) .

Specifically for having different implementations for Browser/Node.js check PersistentReaderWriter.js.

Once you have some template to start working with browserify, and you're aware of some pitfalls, you might find you'd like to use it for small libraries as well.

Edit: Note that if you browserify the module, the isBrowser() check shouldn't be by checking if module and module.exports are defined, since Browserify's wrapper will define them in the module context. Instead, in my example, I check for window to be defined.

Upvotes: 6

Roemer
Roemer

Reputation: 3576

I found a solution, although it is not what is first had in mind, with different build environments.

In my case, I had a pretty small library which were using the Node's http, https, querystring and url packages. I did not want to use something like Browserify, because it seemed not appropriate to bundle all these packages for a small api-library. Instead I replaced the http and https functionality with the XMLHttpRequest package. The small functionalities provided by querystring and url could easily be rewritten.

In my library I check, run-time, if the window.XMLHttpRequest object is available. If so, use that (native) object. Otherwise, it uses the one provided by the package. As such:

_getRequestObject: () ->
  if window? and window.XMLHttpRequest?
    return new window.XMLHttpRequest()

  if window? and window.ActiveXObject?
    try
      request = new ActiveXObject('Msxml2.XMLHTTP')
    catch e
      try
        request = new ActiveXObject('Microsoft.XMLHTTP')
      catch e

  XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
  return new XMLHttpRequest()

An other problem was that exports is not defined in the browser. There are packages to simulate this behaviour, but again, it did not want to bloat the library. So I check, again runtime, if the module variable is set. If so, the object I have defined is set to that, otherwise it is set to the window object:

if typeof module is 'undefined'
  window['My-module'] = My_module_object
else
  module.exports = exports = My_module_object

This all works for me, because I have no real required dependencies of Node.js which are not present in the browser environment. I am afraid that with larger projects a solution like Browserify is really needed, but I am still curious if there are other solutions, like creating different build-environments when packaging the library for Node.js or when for (e.g.) Bower.

Upvotes: 0

Related Questions