Brief History of

JavaScript

Modules

 

 

 

 

 

@sychoi
SungTheCoder

About me

  • Web Developer
  • Coder
  • Programmer
  • Software Engineer

~ 7 years of Web Development

Many many years ago

Demo App

Source Code

HTML

<!DOCTYPE html>
<html>
 <head>
   <meta charset="UTF-8">
   <title>JS Modules</title>
 </head>
 <body>
   <h1>
     The Answer is
     <span id="answer"></span>
   </h1>
 </body>
</html>

Source Code

JavaScript - Main

var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document.getElementById("answer").innerHTML = answer;

Source Code

JavaScript - sum

function sum(arr){
  return reduce(arr, add);
}

Source Code

JavaScript - add

function add(a, b) {
  return a + b;
}

Source Code

JavaScript - reduce

function reduce(arr, iteratee) {
  var index = 0,
    length = arr.length,
    memo = arr[index];
  for(index += 1; index < length; index += 1){
    memo = iteratee(memo, arr[index])
  }
  return memo;
}

main

sum

add

reduce

Inline Script

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script type="text/javascript">
      function add(a, b) {
        return a + b;
      }
      function reduce(arr, iteratee) {
        var index = 0,
          length = arr.length,
          memo = arr[index];
        for(index += 1; index < length; index += 1){
          memo = iteratee(memo, arr[index])
        }
        return memo;
      }
      function sum(arr){
        return reduce(arr, add);
      }
      /* Main Function */
      var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
      var answer = sum(values)
      document.getElementById("answer").innerHTML = answer;
    </script>
  </body>
</html>

add

reduce

sum

main

  • Lack of Code Reusability
  • Pollution of global namespace
  • Lack of Dependency Resolution

Script Tags

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script type="text/javascript" src="./add.js"></script>
    <script type="text/javascript" src="./reduce.js"></script>
    <script type="text/javascript" src="./sum.js"></script>
    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>

{

}

add.js

//add.js
function add(a, b) {
  return a + b;
}

reduce.js

//reduce.js
function reduce(arr, iteratee) {
  var index = 0,
    length = arr.length,
    memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index])
  }
  return memo;
}

sum.js

//sum.js
function sum(arr){
  return reduce(arr, add);
}

main.js


// main.js
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document
    .getElementById("answer")
    .innerHTML = answer;
  • Lack of Code Reusability
  • Pollution of global namespace
  • Lack of Dependency Resolution

Module Object & IIFE

Module Pattern

my-app.js

//my-app.js
var myApp = {};

Exposes one object

IIFE

(function(){
    /*... your code goes here ...*/
})();

Immediately-Invoked Function Expression

add.js

//add.js
(function(){
  myApp.add = function(a, b) {
    return a + b;
  }  
})();

reduce.js

(function(){
  myApp.reduce = function(arr, iteratee) {
    var index = 0,
      length = arr.length,
      memo = arr[index];
  
    index += 1;
    for(; index < length; index += 1){
      memo = iteratee(memo, arr[index])
    }
    return memo;
  }  
})();

sum.js

(function(){
  myApp.sum = function(arr){
    return myApp.reduce(arr, myApp.add);
  }  
})();

main.js

(function(app){
  var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
  var answer = app.sum(values)
  document
    .getElementById("answer")
    .innerHTML = answer;
})(myApp);

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script type="text/javascript" src="./my-app.js"></script>
    <script type="text/javascript" src="./add.js"></script>
    <script type="text/javascript" src="./reduce.js"></script>
    <script type="text/javascript" src="./sum.js"></script>
    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>

{

}

  • Lack of Code Reusability
  • Pollution of global namespace
  • Lack of Dependency Resolution

CommonJS

  • Formerly ServerJS 
  • Standardization Organization
  • Defines Common APIs for web servers, desktop and command line applications.

Export Syntax

//app.js
module.export = function add(a, b) {
    return a + b;
};

Import Syntax

var add = require('./app.js');
  • Synchronous Syntax
  • Not suitable for browser application

AMD

Asynchronous Module Definition

CommonJS Module/Transfer/C format

Export/Import

define([‘add’, ‘reduce’],
    function(add, reduce){
        return function(){...};
    }
);

RequireJS

JavaScript Module Loader

  • Download 'require.js' from http://requirejs.org
  • Add it to <script> tag

How to

main.js

// main.js
define(['sum'], function(sum){
  var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
  var answer = sum(values)
  document
    .getElementById("answer")
    .innerHTML = answer;
})

No Return Statement

sum.js

// sum.js
define(['add', 'reduce'], function(add, reduce){
  var sum = function(arr){
    return reduce(arr, add);
  };

  return sum;
})

add.js

// add.js
define([], function(){
  var add = function(a, b){
    return a + b;
  };

  return add;
});

reduce.js

// reduce.js
define([], function(){
  var reduce = function(arr, iteratee) {
    var index = 0,
      length = arr.length,
      memo = arr[index];

    index += 1;
    for(; index < length; index += 1){
      memo = iteratee(memo, arr[index])
    }
    return memo;
  }

  return reduce;
})

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script data-main="main" src="require.js"></script>
  </body>
</html>
  • Lack of Code Reusability
  • Pollution of global namespace
  • Lack of Dependency Resolution
  • AMD syntax too verbose
  • List of dependencies in the array must match the list of arguments in the function.
  • With current browsers(HTTP 1.1), loading many small files can degrade the performance.

Browserify

JavaScript Module Bundler

  • Allow us to use CommonJS module syntax for browser application
  • Module Bundler
  • Traverse dependency tree of the code and bundle them up into a single file

Install Browserify

npm install -g browserify

main.js

//main.js
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)

document
    .getElementById("answer")
    .innerHTML = answer;

sum.js

//sum.js
var reduce = require('./reduce');
var add = require('./add');

module.exports = function(arr){
  return reduce(arr, add);
};

add.js

//add.js
module.exports = function add(a,b){
    return a + b;
};

reduce.js

//reduce.js
module.exports = function reduce(arr, iteratee) {
  var index = 0,
    length = arr.length,
    memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index])
  }
  return memo;
};

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script src="bundle.js"></script>
  </body>
</html>

?

bundle.js?

$ brwoserify main.js -o bundle.js

bundle.js

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){
module.exports = function add(a,b){
    return a + b;
};

},{}],2:[function(require,module,exports){
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)

document.getElementById("answer").innerHTML = answer;

},{"./sum":4}],3:[function(require,module,exports){
module.exports = function reduce(arr, iteratee) {
  var index = 0,
    length = arr.length,
    memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index])
  }
  return memo;
};

},{}],4:[function(require,module,exports){
var reduce = require('./reduce');
var add = require('./add');

module.exports = function(arr){
  return reduce(arr, add);
};

},{"./add":1,"./reduce":3}]},{},[2]);

Building Library?

What Module type?

  1. Global Object (Module Pattern)
  2. CommonJS
  3. AMD
  4. All of the above

UMD

Universal Module Definition

sum.umd.js

//sum.umd.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['add', 'reduce'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS-like
        module.exports = factory(require('add'), require('reduce'));
    } else {
        // Browser globals (root is window)
        root.sum = factory(root.add, root.reduce);
    }
}(this, function (add, reduce) {
    //  private methods

    //    exposed public methods
    return function(arr) {
      return reduce(arr, add);
    }
}));

Your Next Application?

  1. Global Object (Module Pattern)
  2. CommonJS
  3. AMD
  4. UMD
  5. None of above

ES6 Module

import
export

main.js

// main.js
import sum from "./sum";

var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);

document
    .getElementById("answer")
    .innerHTML = answer;

sum.js

// sum.js
import add from './add';
import reduce from './reduce';

export default function sum(arr){
  return reduce(arr, add);
}

add.js

// add.js
export default function add(a,b){
  return a + b;
}

reduce.js

//reduce.js
export default function reduce(arr, iteratee) {
  let index = 0,
  length = arr.length,
  memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index]);
  }
  return memo;
}
  • Concise Syntax
  • Future of JavaScript

Not Supported by all the browsers

Webpack

  • Module Bundler
  • Support CommonJS, AMD
  • ES6 Module can be supported
  • More features
    • Code Split
    • Loader
    • Plugin 
    • Webpack DevServer

Webpack

Getting Started

$ mkdir project; cd project
$ npm init -y
$ npm install -D webpack webpack-dev-server

webpack.config.js

module.exports = {
  entry: './app/main.js',
  output: {
    path: './dist',
    filename: 'bundle.js'
  }
}

package.json

                          :
                          :
"scripts": {
    "start": "webpack-dev-server -progress -colors",
    "build": "webpack"
 },
                          :
                          :

app/main.js

// app/main.js
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)

document
    .getElementById("answer")
    .innerHTML = answer;

app/sum.js


// app/sum.js
define(['./reduce', './add'],
    function(reduce, add){
      sum =  function(arr){
        return reduce(arr, add);
      }
    
      return sum;
    }
);

app/add.js

// app/add.js
module.exports = function add(a,b){
    return a + b;
};

app/reduce.js

// app/reduce.js
module.exports = function reduce(arr, iteratee) {
  var index = 0,
    length = arr.length,
    memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index])
  }
  return memo;
};

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script src="dist/bundle.js"></script>
  </body>
</html>
$ npm start
$ ls dist/
$
$ npm build
$ ls dist/
bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}


/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;

/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;

/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

	var sum = __webpack_require__(1);
	var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
	var answer = sum(values)

	document.getElementById("answer").innerHTML = answer;


/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

	var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(2), __webpack_require__(3)], __WEBPACK_AMD_DEFINE_RESULT__ = function(reduce, add){
	  sum =  function(arr){
	    return reduce(arr, add);
	  }

	  return sum;
	}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ },
/* 2 */
/***/ function(module, exports) {

	module.exports = function reduce(arr, iteratee) {
	  var index = 0,
	    length = arr.length,
	    memo = arr[index];

	  index += 1;
	  for(; index < length; index += 1){
	    memo = iteratee(memo, arr[index])
	  }
	  return memo;
	};


/***/ },
/* 3 */
/***/ function(module, exports) {

	module.exports = function add(a,b){
	    return a + b;
	};


/***/ }
/******/ ]);

Rollup

Rollup

  • Module Bundler
  • Support CommonJS, AMD and ES6
  • Tree Shaking

Getting Started

$ npm install -g rollup

main.js

// main.js
import sum from "./sum";

var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);

document
    .getElementById("answer")
    .innerHTML = answer;

sum.js

// sum.js
import { add } from './math';
import reduce from './reduce';

export default (arr) => reduce(arr, add);

reduce.js

// reduce.js
export default (arr, iteratee) => {
  let index = 0,
  length = arr.length,
  memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index]);
  }
  return memo;
}

math.js

let add = (a,b) => a + b;
let sub = (a,b) => a - b;

export { add, sub };

Build

$ rollup main.js -o bundle.js
let add = (a,b) => a + b;

var reduce = (arr, iteratee) => {
  let index = 0,
  length = arr.length,
  memo = arr[index];

  index += 1;
  for(; index < length; index += 1){
    memo = iteratee(memo, arr[index]);
  }
  return memo;
}

var sum = (arr) => reduce(arr, add);

var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);

document.getElementById("answer").innerHTML = answer;

SystemJS

SystemJS

  • Module Loader
  • Support CommonJS, AMD and ES6
  • Build on top of ES6 module loader polyfill
  • The syntax and API will most likely to be part of the language - Future proof

To Load a Module

<script src="system.js"></script>
<script>
  System.import('/js/main.js')
    .then(thing => {
     /* do something with thing */
     });
</script>

Asynchronously

JSPM

JSPM

  • Package Manager
  • Module Loader
  • Module Bundler
  • Support CommonJS, AMD and ES6
  • Dev Server

Installing 3rd party modules

$ jspm install npm:package-name 
$ jspm install github:package/name

will download the package from ‘npm’ or ‘github’ and install it on ‘jspm_packages’ directory

jspm-server

  • Development Server
  • Detects code change and reloads browser
  • Make use of SystemJS module loader

bundler

$ jspm bundle main.js bundle.js

Under the hood, JSPM uses Rollup for bundling

PARCEL

  • 🚀 Blazing fast bundle times - multicore compilation, and a filesystem cache for fast rebuilds even after a restart.
  • 📦 Out of the box support for JS, CSS, HTML, file assets, and more - no plugins to install.
  • 🐠 Automatically transforms modules using Babel, PostCSS, and PostHTML when needed - even node_modules.
  • ✂️ Zero configuration code splitting using dynamic import() statements.
  • 🔥 Built in support for hot module replacement
  • 🚨 Friendly error logging experience - syntax highlighted code frames help pinpoint the problem.

Install parcel

npm install -g parcel-bundler
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script src="app/main.js"></script>
  </body>
</html>

index.html

$ parcel index.html

Development Server

$ parcel build index.html
✨  Built in 274ms.

dist/main.1054a117.map     1.57 KB     6ms
dist/main.1054a117.js      1.99 KB    13ms
dist/index.html              201 B     5ms

$ ls -lh dist
-rw-r--r--  1 sung  staff   201B index.html
-rw-r--r--  1 sung  staff   2.0K main.1054a117.js
-rw-r--r--  1 sung  staff   1.6K main.1054a117.map

Production Build

$ cat dist/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>JS Modules</title>
  </head>
  <body>
    <h1>
      The Answer is
      <span id="answer"></span>
    </h1>

    <script src="/main.1054a117.js"></script>
  </body>
</html>

dist/index.html

Source Maps

dist/main.1054a771.js

arcelRequire=function(e,r,n,t){var i="function"==typeof parcelRequire&&parcelRequire,o="function"==typeof require&&require;function u(n,t){if(!r[n]){if(!e[n]){var f="function"==typeof parcelRequire&&parcelRequire;if(!t&&f)return f(n,!0);if(i)return i(n,!0);if(o&&"string"==typeof n)return o(n);var c=new Error("Cannot find module '"+n+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[n][1][r]||r},p.cache={};var l=r[n]=new u.Module(n);e[n][0].call(l.exports, p,l,l.exports,this)}return r[n].exports;function p(e){return u(p.resolve(e))}}u.isParcelRequire=!0,u.Module=function(e){this.id=e,this.bundle=u,this.exports={}},u.modules=e,u.cache=r,u.parent=i,u.register=function(r,n){e[r]=[function(e,r){r.exports=n},{}]};for(var f=0;f<n.length;f++)u(n[f]);if(n.length){var c=u(n[n.length-1]);"object"==typeof exports&&"undefined"!=typeof module?module.exports=c:"function"==typeof define&&define.amd?define(function(){return c}):t&&(this[t]=c)}return u}({"OwWD":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.add=void 0;var e=function(e,r){return e+r};exports.add=e;
},{}],"y4BJ":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var e=function(e,t){var r=0,o=e.length,u=e[r];for(r+=1;r<o;r+=1)u=t(u,e[r]);return u};exports.default=e;
},{}],"IY9c":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var e=require("./add"),r=t(require("./reduce"));function t(e){return e&&e.__esModule?e:{default:e}}var u=function(t){return(0,r.default)(t,e.add)};exports.default=u;
},{"./add":"OwWD","./reduce":"y4BJ"}],"PFbx":[function(require,module,exports) {
"use strict";var e=r(require("./sum"));function r(e){return e&&e.__esModule?e:{default:e}}var t=[1,2,4,5,6,7,8,9],u=(0,e.default)(t);document.getElementById("answer").innerHTML=u;
},{"./sum":"IY9c"}]},{},["PFbx"], null)

//# sourceMappingURL=/main.1054a771.map

dist/main.1054a771.map

{ 
   "version":3,
   "sources":["app/add.js", "app/reduce.js", "app/sum.js", "app/main.js"],
   "names":["add", "a", "b", "arr", "iteratee", "index", "length", "memo",
      "values", "answer", "document", "getElementById", "innerHTML" ],
   "file":"main.1054a771.map",
   "sourceRoot":"..",
 "mappings":";AAAA,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,SAAA,EAAA,IAAIA,EAAM,SAACC,EAAEC,GAAMD,OAAAA,EAAIC,GAAvB,QAAA,IAAA;;ACUC,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,aAAA,EAVc,IAAA,EAAA,SAACC,EAAKC,GACfC,IAAAA,EAAQ,EACZC,EAASH,EAAIG,OACbC,EAAOJ,EAAIE,GAGLA,IADNA,GAAS,EACHA,EAAQC,EAAQD,GAAS,EAC7BE,EAAOH,EAASG,EAAMJ,EAAIE,IAErBE,OAAAA,GACR,QAAA,QAAA;;ACPc,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,aAAA,EAHf,IAAA,EAAA,QAAA,SACA,EAAA,EAAA,QAAA,aAEe,SAAA,EAAA,GAAA,OAAA,GAAA,EAAA,WAAA,EAAA,CAAA,QAAA,GAAA,IAAA,EAAA,SAACJ,GAAQ,OAAA,EAAOA,EAAAA,SAAAA,EAAKH,EAAZ,MAAT,QAAA,QAAA;;ACEf,aALA,IAAA,EAAA,EAAA,QAAA,UAKA,SAAA,EAAA,GAAA,OAAA,GAAA,EAAA,WAAA,EAAA,CAAA,QAAA,GAHA,IAAIQ,EAAS,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAChCC,GAAS,EAAID,EAAAA,SAAAA,GAEjBE,SAASC,eAAe,UAAUC,UAAYH"
}

https://sourcemaps.info/spec.html

http://slides.com/sungthecoder/js-module-history

https://github.com/sungthecoder/js-modules-examples