Using third-party JavaScript file in Node.js - javascript

I have a JavaScript file which is not written for Node.js. It is actually written by me, but this question may be more understandable if you think of it as a third-party library.
Normally, a Node.js module only exposes the module.exports object to external code. This requires the file to be written with Node.js in mind.
However, the file isn't written for Node.js, yet I want to include it into a Node.js module. I can't edit this file, since it will be used later, independently of Node.js.
I imagine I could pull it off by reading the file and calling eval on the contents, but is there a better way?
TL;DR
How can I include a .js file into a Node.js module, without rewriting it to use Node.js-specific code like exports?
P.S. The reason I want to use it in Node.js is for a few tests before it's deployed elsewhere.

It depends on how the code is written on that file. If the file contains a class or a single object then you can do this:
(function(global) {
function MyClass() {
}
MyClass.prototype = {
...
};
if( typeof module !== "undefined" && module.exports ) {
module.exports = MyClass;
}
else if ( global ) {
global.MyClass = MyClass;
}
})(this);
You can modify the global assignment to be a deeper namespace instead if required.

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
}

Publishing to NPM and CDN

This is my first time writing a very basic JavaScript library and now I intend to publish it. I wrote it as a function and users can simply import the JavaScript file over a CDN. I intend to publish this to NPM too, so users can use it with JS bundlers too.
I created another file to match the Node.js format, basically, I added module.exports = before my function and that's all working. So, now I have ended up with two files with basically the same code, one for CDN and one for NPM. Is there any way to unify this, basically merge it all in the same file?
I would really like to know how this is usually done. When a function is written to be imported as a CDN, how does it get converted to a Node Module? Is creating a separate file the only way, or am I missing something?
This question is easily answered with a quick google search, but I thought it an interesting and well written question nonetheless so I'm transcribing some of the results here to help anyone else:
From this blog post:
UMD
UMD (Universal Module Definition) is a block of code we can use to wrap around our library. This block of code makes it possible to use a library both on the frontend and in Node.
It kinda looks like this:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['b'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node.
module.exports = factory(require('b'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.b);
}
}(typeof self !== 'undefined' ? self : this, function (b) {
// Use b in some fashion.
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return {};
}));
You can read more about UMD here.
Usually I would recommend you use a bundler / build tool to automate this for you and make the code available in even more environments.
Webpack is a a good choice for this.

How to compile a TypeScript project to a single JS file so it is usable in browser

I have to write a Javascript SDK for a little project I am working on. To do that, I had thought of creating a TypeScript project and compiling it to a single Javascript file, so the users of my SDK could just inject that file in their web pages.
However, I just came to know that if I use import, and try to compile to a single file, then it only supports SystemJS.
So, how to compile a TypeScript project to a single JS file so it is usable in browser?
By usable in browser, I mean that if I create a class App in TypeScript, then I could do this in dev console:
var x = new App();
I have been at this for more than a hour now, and everything I have found seems to suggest that this is not possible.
Edit: This doesn't really answer my question. Like I said in the example, I need the functionality that if there is a class called App in my TypeScript project, it should be visible to the browser with the same name, so I could do var x = new App() in my dev console. (Or a user can do this in his JS file that he injects after injecting my SDK file). That answer is just telling how to create an outfile in SystemJS.
For this you can use webpack, it is a Node.JS utility that attempts to bundle Node.JS-like modules. Webpack doesn't automatically export modules to the global object, but generates (or attempts to generate) a function that replace the Node.JS's default require, which is used to execute the entry module and others, thus you can modify this function for exporting each module (or properties of each module) in the global object.
(In TypeScript, use the CommonJS module. Second, install and use the ts-loader plugin among with webpack, so you'll directly compile TypeScript from webpack.)
Maybe that applies to Webpack 2. For example, you modify the __webpack_require__ function. It is not inside the global object and thus you must interfere in the webpack's generated source code, at function __webpack_require__:
function __webpack_require__(moduleId) {
// [...] (After the `if (installedModules...) ...`)
/*
* You don't have access to the module name, so export each
* property to the browser's global object.
*/
var exports = module.exports;
for (var key in exports)
window[key] = exports[key];
}

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!

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.
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.

Categories

Resources