JS Classes Example

Server (NodeJS) Side:

////////// _myDummyModule.js

// A really bad usecase is demonstrated here.
var count = 1;
exports.increment = function() {
    count++;
};
exports.getCount = function() {
    return count;
};
////////// _myDummyModuleClass.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
// Noob caveat: If you want to OVERWRITE *module.exports*, do it directly!
// (i.e. if you overwrite the 'exports' alias instead, it will be meaningless;
// because exports=module.exports... you should: module.exports=yourNewObject)
module.exports = class Calculator { // Doesn't really matter that it's named
  constructor(initialValue) {
    this.count = initialValue===undefined ? 1 : initialValue;
  }
  increment() { this.count++; }
  getCount() { return this.count; }
}
////////// _myNotesOnJS.js

// module.exports is the variable that gets returned from require().
// 'exports' is just an "alias" to module.exports.
console.log(module.exports == exports); // => true

// Requiring twice can be problematic!
const a = require('./_myDummyModule.js');
const b = require('./_myDummyModule.js');
console.log(typeof a); // => object
console.log(a==b, a===b); // => true true
a.increment(); // We increment a, but b (which is ==a) is also incremented :(
console.log(a.getCount()); // => 2
console.log(b.getCount()); // => 2

// How to use a class
const c = require('./_myDummyModuleClass.js');
c1 = new c();
c2 = new c();
c3 = new c(123);
console.log(typeof c); // => function
console.log(typeof c1); // => object
console.log(c1 == c2); // => false
c1.increment(); // OK: We increment only c1
console.log(c1.getCount(), c2.getCount(), c3.getCount()); // 2 1 123

Client side:

////////// _myDummyClassClientSide.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
// Noob caveat: If you want to OVERWRITE *module.exports*, do it directly!
// (i.e. if you overwrite the 'exports' alias instead, it will be meaningless;
// because exports=module.exports... you should: module.exports=yourNewObject)
module.exports = class Calculator { // Doesn't really matter that it's named
  constructor(initialValue) {
    this.count = initialValue===undefined ? 1 : initialValue;
  }
  increment() { this.count++; }
  getCount() { return this.count; }
}
////////// _myDummyClassLikeClientSide.js
function Apple(appleType) {
    this.appleType = appleType;
    this.color = "red";
}
Apple.prototype.updateColor = function(color) {
  this.color = color;
}
Apple.prototype.getInfo = function() {
    return this.color + ' ' + this.appleType + ' apple';
};
module.exports = Apple;
////////// someClientJS.js
// (note: make sure you enable ES2015 in the gulpfile for this to run...)
const MyDummyClass = require('./_myDummyClassClientSide.js');
let c1 = new MyDummyClass();  // private 'count' initialzied to 1 by default
let c2 = new MyDummyClass(17);
console.log(typeof MyDummyClass); // => function
console.log(typeof c1); // => object
console.log(c1 == c2); // => false
c1.increment(); // OK: We increment only c1
console.log(c1.getCount(), c2.getCount()); // 2 17
// (note: the following is in vanilla JS; no need for ES2015)
var MyDummyClass = require('./_myDummyClassLikeClientSide.js');
console.log(MyDummyClass); // => ƒ n(e){this.appleType=e,this.color="red"}
var a1 = new MyDummyClass();
var a2 = new MyDummyClass('aaaaaaa');
console.log(a1.getInfo()); // => red undefined apple
console.log(a2.getInfo()); // => red aaaaaaa apple
a1.updateColor('newcolor');
console.log(a1.getInfo()); // => newcolor undefined apple
console.log(a2.getInfo()); // => red aaaaaaa apple

Running Apache and Node on the same server

Option 1. Apache in the front, /node/ handled by local Node.

STEP 1: Add the ProxyPass directive to some .conf file:
ProxyPass /node/ http://localhost:8000/
(e.g. save this single line into some file my_proxy_forward_node_to_8000.conf, inside the /etc/apache2/conf-available/ directory, then a2enconf it)
More notes for this to work: (from ProxyPass documentation)
  • Ensure the mods proxy and proxy_http are enabled (a2enmod).
  • If the first argument ends with a trailing /, the second argument should also end with a trailing /, and vice versa. Otherwise, the resulting requests to the backend may miss some needed slashes and do not deliver the expected results.
  • The ProxyRequests directive should usually be set off when using ProxyPass.
STEP 2: Then run Node locally on port 8000:
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Apache!\n');
}).listen(8000, '127.0.0.1');

Option 2. Nginx as reverse proxy to Node (and to Apache)

TODO: Fill in.

Option 3. (not up-to-date; so probably not recommended) TrafficServer in the front, Apache and Node behind it.

STEP 0: Install TrafficServer. (Note: As of 4/2017, this installed version 5.3.x from the repositories, though the real latest version is 7.2.x)
apt-get install trafficserver
IMPORTANT: Edit records.config and add this line (I added in the beginning; I don’t understand why this isn’t the default configuration). If you don’t specify such user_id, it will not agree to run itself at all, because it doesn’t want to run as root.
CONFIG proxy.config.admin.user_id STRING trafficserver
STEP 1: Enable Reverse Proxying (pass incoming traffic to e.g. another port in localhost, preserving “Host:” headers etc.). Edit records.config: (the underlined value here is the only change needed for the default value, as of 4/2017)
CONFIG proxy.config.http.cache.http INT 1
CONFIG proxy.config.reverse_proxy.enabled INT 1
CONFIG proxy.config.url_remap.remap_required INT 1
CONFIG proxy.config.url_remap.pristine_host_hdr INT 1
CONFIG proxy.config.http.server_ports STRING 8080
Edit remap.config:
regex_map http://(.*):8080/ http://localhost:80/
Finally, reread the config files (traffic_line --reread_config or equivalently -x). IMPORTANT: The 8080 and 80 above (in both records.config and remap.config) should be switched if you really want TrafficServer in the front… STEP 2: Log files, monitoring, security: https://docs.trafficserver.apache.org/en/5.3.x/admin/working-log-files.en.html https://docs.trafficserver.apache.org/en/5.3.x/admin/monitoring-traffic.en.html https://docs.trafficserver.apache.org/en/5.3.x/admin/security-options.en.html STEP 3: If you want to disable cache completely: (didn’t check this) Edit record.config:
CONFIG proxy.config.http.cache.http INT 0

(BONUS) STEP: Cache configuration: storage.config. E.g. its default contents were simply a one-liner /var/cache/trafficserver 256M. Changes require restart of trafficserver. It is recommended to use raw devices; see documentation. E.g. how to cache everything (e.g. if you’re serving only static files): By default, Traffic Server will cache an HTTP response only if it contains a Cache-Control or Expires header explicitly specifying how long the item should be stored in the cache. To disable that need for those required headers:
sudo traffic_line --set_var proxy.config.http.cache.required_headers --value 0
sudo traffic_line --reread_config
(BONUS) STEP: Use a tool called Cache Inspector:
sudo traffic_line --set_var proxy.config.http_ui_enabled --value 1
Edit remap.config, add this line at the top of the file:
map http://your_server_ip:8080/inspect http://{cache}
Then restart the trafficserver service.

Gulp

New notes: (4/2018)

The older post content dealt with react as well as ES2015. Since then, lot has changed: I’m not interested in react, and babel prefers working with ‘babel-preset-env’. So:

npm install babelify babel-core babel-preset-env --save-dev

When you get some SyntaxError: Unexpected token: name (xxxxxx) from Uglify (or Browserify?) it is probably because uglify couldn’t parse some JS syntax, like ES2015 syntax. One solution: Babelify, as demonstrated below.

Your gulp task can take a browserify object and transform it using babelify as in the first piped command here: (the original documentation: https://github.com/babel/babelify)
b
  .transform("babelify", {presets: ["env"]}) // Optional: Uncomment to support ES2015
  .bundle()
  .on('error', gutil.log.bind(gutil, 'Browserify Error')) // log errors if they happen
  .pipe(source(bundle_name)) // e.g. 'bundle.js'
  .pipe(buffer()) // optional, remove if you don't need to buffer file contents
  //.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file // optional, remove if you don't want sourcemaps
  // Add transformation tasks to the pipeline here.
  .pipe(uglify())
  .on('error', gutil.log)
  .pipe(sourcemaps.write('./')) // writes .map file
  .pipe(gulp.dest('./public/dist'));

Older post content from here

First note: We’ll eventually be using some of these packages: (also note the --save vs. --save-dev)
npm install --save-dev gulp gulp-nodemon gulp-htmlmin gulp-cssnano gulp-uglify gulp-buster browserify vinyl-source-stream gulp-util babelify babel-preset-es2015 babel-preset-react
npm install --save react react-dom
Ok, hi. Let’s start with gulp‘s .task() simple syntax:
// (in gulpfile.js)
gulp.task('temp', [], function(){
  console.log('hello world');
}
Just copy the above into the file gulpfile.js at your root directory — this is the only file gulp needs in order to run. Then run %> gulp temp and get:
hello world
And here’s how we can simply bundle together a few files, using browserify:
// (in gulpfile.js)
gulp.task('temp', [], function() {
  browserify({
    entries: 'file1.js' // or array, e.g. ['file1.js','file2.js']
  })
  .add('another2.js') // Can also add like this
  .add('another3.js') // And more...
  .add(['another4.js', 'another5.js']) // Or use an array...

  .bundle() // Finally, let's bundle!

  .pipe(source('bundle.js')) // And store result in bundle.js
  .pipe(gulp.dest('./')); // In this directory
Ok, so we have the following simple usage:
// (in gulpfile.js)
gulp.task('temp', [], function() {
  browserify({
    entries: 'file1.js'
  })
  .bundle() // Bundle!
  .pipe(source('bundle.js')) // Store result in bundle.js
  .pipe(gulp.dest('./')); // In this directory
Suppose file1.js contains:
// file1.js
var file1var = 123;
console.log('in file1.js');
Then running %> gulp temp will overwrite bundle.js to contain:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
// file1.js
var file1var = 123;
console.log('in file1.js');

},{}]},{},[1]);
There you have it. The basic usage we’ve been looking for: We can bundle together multiple JS files into one, so that we could include it in from our index.html using some script tag, for example. In our modest case, though, with no index.html and just dummy code, we could run %> node bundle.js to get this output:
in file1.js

It’s time we appreciate what we already have in our hands: What bundle really does is “Bundle the files and their dependencies into a single javascript file.” Let’s see that in action, shall we? Create a file2.js:
var file2var = 456;
console.log('in file2.js');

module.exports = {
  f : function() {
    console.log('My cool function from file2.js');
  },
  c : 789
}
And add an import command to file1.js, so its updated content is:
import File2 from './file2';

// file1.js
var file1var = 123;
console.log('in file1.js');
Running %> gulp temp will FAIL, with some error like the one below, complaining about our ES2015 syntax… (namely our newly added import command in file1.js)
events.js:141
      throw er; // Unhandled 'error' event
      ^
SyntaxError: 'import' and 'export' may appear only with 'sourceType: module'
So to allow ourselves to write in ES2015 and transpile it to ordinary JS, we use babelify! Update our gulp file as follows:
// (in gulpfile.js)
gulp.task('temp', [], function() {
  browserify({
    entries: 'file1.js'
  })
  .transform("babelify", {presets: ["es2015"]}) // <= ADD THIS ROW
  .bundle() // Bundle!
  .pipe(source('bundle.js')) // Store result in bundle.js
  .pipe(gulp.dest('./')); // In this directory
And voila, running %> gulp temp overwrites bundle.js into the following:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';

var _file = require('./file2');

var _file2 = _interopRequireDefault(_file);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// file1.js
var file1var = 123;
console.log('in file1.js');

},{"./file2":2}],2:[function(require,module,exports){
'use strict';

var file2var = 456;
console.log('in file2.js');

module.exports = {
  f: function f() {
    console.log('My cool function from file2.js');
  },
  c: 789
};

},{}]},{},[1]);
Cool! So even though we merely asked for file1.js to be bundled, browserify realized that it depends on file2.js and included it in the bundle! Awesome! The import command in file1.js first "sources" the contents of file2.js. To illustrate, try running %> node bundle.js, you'll get:
in file2.js
in file1.js

Finally, a more closer-to-reality usage is to have file1.js do something with the class / object that we imported from file2.js. For example, suppose file1.js is:
import File2 from './file2';

// file1.js
var file1var = 123;
console.log('in file1.js');

console.log(File2);
Run %> gulp temp and then %> node bundle.js and we get:
in file2.js
in file1.js
{ f: [Function: f], c: 789 }

Read more here on the import command and ways to selectively import only some of the module's members (and even give them shorter aliases if necessary). Read here on browserify. In particular, about require and external!