JavaScript Modules

Dr. Greg Bernstein

Updated September 13th, 2021

JavaScript Modules

ES6 Modules Readings/References

General JavaScript Modules

Why JavaScript Modules

  • Almost all computer languages support breaking programs into more manageable pieces.
  • Need to avoid “name collisions” of variables, functions, etc…
  • Promote code reuse via libraries and frameworks.

State of JavaScript Modules

  • JavaScript initially did not have any module support as part of the language.
  • A number of design patterns were used instead, these tend to be non-optimal, confusing or both.
  • First “de facto” module system was called CommonJS, there were more advanced systems such as AMD, UMD, and now we have standard modules in ES6.

Where Will We Use Modules

ES6 Modules

ES6 Modules Basics

Part of the JavaScript language standard

  • Now working in browsers, long used in front-end frameworks via bundler tools.
  • You choose what to export from a module (file) via the export statement (many variations).
  • You choose what to import to a module via the import statement (many variations).

ES6 Export 1

Explicit export list from foilGear.js

let sails = ["Maui 7.7", "S2 7.0", "Maui 6.3", "Maui 5.5"];
let masts = ["Maui 460", "Maui 430", "Maui 400"];
let board = "Wind Foil by Alex";
let foil = { strut: "1m", frontWing: "80cm", rearWing: "20cm", brand: "F4" };

export { sails, masts, board, foil };

ES6 Export 2

Default export from waveGear.js

export default {
    board: "Fanatic FreeWave 88",
    sails: ["Maui Global 5.8", "North Natural 5.4", "North Natural 5.0"],
    masts: ["Maui Gladiator RDM 430", "North Gold 400"],
    boom: "Maui Carbon Wave"
};

ES6 Export 3

Functions are easy to export from conditions.js

function wind() { // random wind in MPH
    return Math.random() * 25;
};

function tide() { // random tide in feet
    return Math.random() * 6;
};

export { tide, wind };

ES6 Import

Browser use (with an HTTP server)

<script type="module"> // Must have "module" for this to work
  import * as foilGear from './foilGear.js'; 
  import waveGear from './waveGear.js'; 
  import {tide, wind} from './conditions.js'; 
  console.log("Stuff I imported from foilGear.js:"); 
  console.log(foilGear); 
  console.log("Stuff I imported from waveGear.js:"); 
  console.log(waveGear);
  console.log("Conditions");
  console.log(`wind: ${wind().toFixed(1)} mph,`);
  console.log(`tide level ${tide().toFixed(1)} feet`);
</script>

Node.js with ES6 Modules

Node.js Support

Node.js added support for ES6 (ECMAScript) modules in version 13. Hence version 14 is the first LTS (long term support) version with this support so we will start using it.

Node.js and *.mjs Files

By default Node.js treats files ending in .mjs as modern ES6 modules and interprets import and export according to the JavaScript standards. Files ending .js are interpreted as regular Node.js and interprets require and exports “keywords” according to CommonJS module conventions. Hence we will use .mjs file extensions in this class for most of our Node.js work.

Node.js ES6 import example

From the file nodeES6Ex.mjs:

import * as foilGear from './foilGear.mjs';
import waveGear from './waveGear.mjs';
import { tide, wind } from './conditions.mjs';
console.log("Stuff I imported from foilGear.mjs:");
console.log(foilGear);
console.log("Stuff I imported from waveGear.mjs:");
console.log(waveGear);
console.log("Conditions");
console.log(`wind: ${wind().toFixed(1)} mph,`);
console.log(`tide level ${tide().toFixed(1)} feet`);

Bundlers

What Do They Do

  • Bundlers are just used to combine many JavaScript modules into a few JavaScript modules for deployment purposes.

  • Modern bundlers understand ES6 module syntax

  • The output files from a bundler are then included in an HTML page with <script> and <link> tags as appropriate.

  • This relieves the browser of module management for now.

Other Bundler Features

Modern JavaScript Bundlers provide more functions such as

  • CSS bundling, code and CSS size minimization
  • JSON integration
  • Development Server, File Watcher, Automatic rebuild

How Do They Work?

Extremely high level view:

Bundlers look at file dependencies such as indicated by <import>, <script>, <link> tags, ES6 import statements, and more. They build up a dependency graph of the various files and then get to work combining them appropriately.

Webpack

Webpack Overview

Rollup.js

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized format for code modules included in the ES6 revision of JavaScript, instead of previous idiosyncratic solutions such as CommonJS and AMD.

Parcel

We’ll use Parcel (v2) in this class.

Blazing fast, zero configuration web application bundler

  • Zero or near zero configuration
  • Global install rather than per project
  • Development Features: file watcher, hot module replacement, web server

Parcel Install and Use

  • Local Installation: npm install --save-dev parcel
  • Getting Started
  • Run it with: node_modules/.bin/parcel index.html

Useful Parcel Options

  • Port for development server:

parcel <your entry file> -p <port number>

  • Production with relative URLs:

parcel build <your entry file> --public-url ./

Parcel package.json Entries

For convenience add these to your package.json:

    "source": "index.html",
    "scripts": {
        "start": "parcel",
        "build": "parcel build --public-url ./",
    },

Use package.json Scripts

  • Development build: npm start
  • Production build: npm run-script build

Parcel Example HTML

From the file BundlerEx/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>Bundler Based ES6 Module Example</title>
</head>

<body>
    <h1>Bundler Based ES6 Module Test</h1>
    <p>Open the developer console to see.</p>
    <script src="main.js" type="module"></script>
</body>

</html>

Parcel Example Main JavaScript

main.js imports all the others JavaScript files like in the first ES6 example

import * as foilGear from './foilGear.js';
import waveGear from './waveGear.js';
import { tide, wind } from './conditions.js';
console.log("Stuff I imported from foilGear.js:");
console.log(foilGear);
console.log("Stuff I imported from waveGear.js:");
console.log(waveGear);
console.log("Conditions");
console.log(`wind: ${wind().toFixed(1)} mph,`);
console.log(`tide level ${tide().toFixed(1)} feet`);

Source to Parcel.js

Notice multiple JavaScript files

Directory with Source

Parcel.js Deployment Build

On server bundlerTest

Dist Directory

Node.js CommonJS Style

Node.js Modules

  • Based on require() method and module.exports object.
  • Assign external module to a variable via require(path) function.
  • Determine what to export from a module by adding to the the module.exports object.

Node.js Export 1

Just add extra fields to the module.exports object:

module.exports.wind = 20;
module.exports.tide = 5.2;
module.exports.board = "Wind Foil by Alex";
module.exports.foil = {strut: "1m", frontWing: "80cm", rearWing: "20cm"};

Node.js Import 1

Use require() function to assign exported “stuff” to a variable:

const windsurf = require('./simpleCJSa');

console.log("Stuff I imported from simpleCJSa.js:");
console.log(windsurf.board);
console.log(windsurf.foil.strut);

Node.js Export 2

Reassign module.exports to an new object

module.exports = {
    wind(){
        return Math.random()*25;
    },
    kite: "12m",
    helmet: true,
    lines: "25m",
    bar: "North control bar"};

Node.js Import 2

Same use of require() function

const kitesurf = require('./simpleCJSb');

console.log("Stuff I imported from simpleCJSb.js:");
console.log(`wind: ${kitesurf.wind().toFixed(1)} mph`);
console.log(kitesurf.kite);

Node.js Export 3

Shortcut via exports object

exports.extra = "Hello CommonJS Modules";
exports.tide = function() {
                return Math.random()*6;
};

Node.js Import 3

Same use of require() function

const stuff = require('./simpleCJSc');

console.log("Stuff I imported from simpleCJSc.js:");
console.log(stuff.extra);
console.log(`tide level ${stuff.tide().toFixed(1)} feet`);
// reveal.js plugins