Build package for both node and browser environment - javascript

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.

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.

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/

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;
}
}

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.

Related

How to make a JavaScript library which can work with Browser, NodeJS and Single Page Applications (React etc.)

So what I want to ask is: Is there a way to convert this simple file into a library which can work with Browsers (script tag), Node JS and Single Page Applications using single codebase?
Until now, all I have been doing was using libraries but it never came to my mind that it isn't actually that simple to make one. I am working on a React application where I created a simple helper file with exports. I thought "why not make it an independent library in NPM?". Just as I started testing it independently in NodeJS environment, I came to realize that there is actually a lot of difference in the way both environments make imports.
I have slight knowledge of Webpack, but I don't know how to approach it. Would I need different codes for different environments?
Another thing that confused me was when I thought about "How do we actually import things/functions from libraries?" Like when we install any library from NPM INSTALL and we do "import { abc } from 'library'", does it look for an index.js file in the library folder or what? Or in case of Node, "let lib = require('library')", where it does it look since its a different environment than SPA?
In conclusion, I have a simple single file I want to launch in NPM as a library which could work in any environment.
This is a very broad question, so a very broad answer: Look at tools like webpack, rollup, and browserify which are often used to make Node.js things available in the browser.
Another possibility is to look at how a module like slug is coded such that it works in the browser and in Node.js, but requires some hacks (and a decent test setup) to do it. For example, it checks typeof window !== 'undefined' to switch between code that needs to run in the browser vs. code that needs to run in Node.js. And this bit at the end detects the module system being used and acts accordingly:
if (typeof define !== 'undefined' && define.amd) { // AMD
define([], function () { return slug })
} else if (typeof module !== 'undefined' && module.exports) { // CommonJS
module.exports = slug
} else { // Script tag
root.slug = slug
}

What are the best practices for writing and organizing javascript plugins?

So I have a nice chunk of code that is the same across many different javascript files, but I occasionally have to update it due to path changes or other various conditions. Now copy and pasting it all over to the new files works fine but is annoying to execute. Is there a good way to maintain one javascript file with my "plugin" code and have it be accessible by other javascript files that use the plugin?
I am looking for both a good nodejs solution, and vanilla js solution. If they could be mutually shared that'd be ideal, but not required by any means. Ideally, I'd like to host my workspace in workspace/ and have some folders, workspace/front-end-js/ and workspace/back-end-nodejs/, be able to run code off a plugin in workspace/plugins/ so that I can execute things like MyPluginVar.Foo();
I am aware of some systems, like the node's var foo = require('bar'); and the frontend browserified version, but really do not know all my options. What's the best way of writing and organizing javascript plugins?
--
Edit: I'm really trying to avoid npm, but it might be the best option.
You typically add your shared libraries and plugins as dependencies to your project's package.json file and install them using npm.
CommonJS modules, which use the module.exports object and require function, are the de facto standard at the moment.
ES2015 modules, which use the export and import statements, are an emerging formal standard, but support for them is not yet universal. They are a good option if your environment supports them.
To load either type of module on the front end, you will need to use a bundler such as Webpack or Browserify.
Older javascript modules typically publish to the global scope (window), and are a simple option for front end code.
You can also support multiple module systems if you like by using a UMD (Universal Module Definition) wrapper. Here's an example from the UMD Github repo that leverages CommonJS if supported, while falling back to a browser global:
(function (root, factory) {
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports, require('b'));
} else {
// Browser globals
factory((root.commonJsStrictGlobal = {}), root.b);
}
}(this, function (exports, b) {
// b represents some dependency
// attach properties to the exports object to define
// the exported module properties.
exports.action = function () {};
}));

How to handle window object on nodejs for server-side rendering of reactjs application

I am trying to implement server-side rendering for my reactjs application.
I am using webpack to build reactjs application, enhanced-resolve to handle importing of jsx files in nodejs.
My application depends on third party libraries like enquire.js.
When react application tries to import enquire.js on nodejs, it fails with error
ReferenceError: window is not defined
Since window object is not available nodejs
how to handle libraries that use window for server side rendering ?
I haven't used Webpack yet. However, I had similar issue using browserify and I solved it by creating two builds: one for browser one for node.js with list of modules to ignore. You could achieve the same effect by adding to webpack config:
{
plugins: [
new webpack.IgnorePlugin(/file\.js$/)
]
}
Then you have to make sure that you are not making any calls to enquire.js by checking if require() returned false value or object with module functions.
Well this is quite old question still if some one is looking at it.
Try
if (typeof window === 'undefined') {
global.window = {};
}

Frontend Development Workflow

I'm curious if anyone else is using a similar approach to mine and or if anyone has a thought or suggestion to help improve the workflow. To my knowledge, this is a somewhat original method.
So basically, I'm using NodeJS to development my client side code. I use Grunt+watch to automatically concatenate and minify all of my projects source into a single .min file, which gets written to the public/js folder. However, for testing I use Jasmine-node and so I need to have an index file (for this purpose only) which requires all of the project source files. However, because I want the same code to run in both Node as well as the browser, I add everything to a global project namespace such as:
myApp.framework.someClass
Within the myapp.js file, I then export to node (if module + exports exists). This allows all of my objects to be exposed to node without having to add the module.exports stuff to every source file, and instead, I can either simple do:
(function() { myApp.framework.someClass = function() {...} })();
in each file, or even simply:
myApp.framework.someClass = function() {...}
This works incredibly well for the client because everything is already there in memory, however the downside is that in Node, sometimes trying to access another class within the myApp namespace will fail because I'm not using something like RequireJS to manage dependencies.
My thought was maybe to add requires to the files just as you would in a standard node app, but then in the build process, have any node specific stuff, stripped out.
Thoughts / comments please!
I have seen tricks like this being used to make the JS code be used in Node and browser environments. This is from async library:
// Node.js
if (typeof module !== 'undefined' && module.exports) {
module.exports = async;
}
// AMD / RequireJS
else if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return async;
});
}
// included directly via <script> tag
else {
root.async = async;
}
https://github.com/caolan/async/blob/master/lib/async.js
Hope that helps!

Is it possible to make JavaScript module compatible with both NodeJS and RequireJS?

I have been investigating how various module concepts can be applied within NodeJS and browser applications using the the NodeJS require (obviously in NodeJS apps) and RequireJS for the web browser environment.
It then dawned on me that some modules may be useful for use by both the client and server applications and thus could be reused.
How can modules be developed so that they are compatible with both of these environments?
One is synchronous and one asynchronous. My first thought was to utilise the asynchronous syntax and then to define a custom module for NodeJS which simply invokes the asynchronous callback, synchronously. But how would the RequireJS-emulator be included into the cross-environment module without first using the NodeJS synchronous callback?
See this post : Bridging the module gap between Node.js and browsers
See also the set of boilerplates at https://github.com/umdjs/umd
About async vs. sync -- for define() in Node, it is common to just use synchronous execution of the factory function passed to define. That is how requirejs works when running in Node.
The http://uRequire.org project bridges the gaps of the AMD & nodejs / commonJs formats. U can write in either (or both), and execute / deploy to any of the two or a standalone.js.
check this resource here: it's Not Hard: Making Your Library Support AMD and CommonJS it explains everything very well
I will post the take-away code you need, but to understand everything you should read that article
by this code, you added AMD(requireJs) and Node support for your js library
(function (global, factory) {
if (typeof define === 'function' && define.amd)
define(['jQuery'], function ($) {
return (global['toaster'] = factory($))
});
else if (typeof module === "object" && module && typeof module.exports === "object")
module.exports = (global['toaster'] = factory(require('jquery')));
else global['toaster'] = factory(global['jQuery']);
})(this, function ($) {
// implementation goes here
var myModule = {};
return myModule;
function helper() {
}
})
one more thing, I found out this Universal Module Definition GitHub project for all variant implementations you can check them all

Categories

Resources