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

Brief History of JS modules

By Sungyeol Choi

Brief History of JS modules

Are you new to JavaScript and confused about module, module loader and module bundler? Or you have been writing JavaScript code for a while, but can’t get a grip of the jargons about module? You heard about the jargons like CommonJS, AMD, Browserify, SystemJS, Webpack, JSPM, etc., but don’t understand why we need them? I will try to explain what are they, and what problem they try to solve, and how they solve the problem.

  • 508
Loading comments...

More from Sungyeol Choi