Frontend Development Workflow - javascript

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!

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.

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 () {};
}));

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.

Using third-party JavaScript file in Node.js

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.

Categories

Resources