2. Production
Incidents
What is a module
Definition from Wikipedia
– single-purpose
– self-contained
– interchangeable
– promote reuse, maintainability, etc.
Separating the functionality of a program into independent, interchangeable modules.
With modular programming, concerns are separated such that modules perform logically discrete
functions, interacting through well-defined interfaces.
3. Production
Incidents
• abstract code: When we call a function, we don’t need to understand the complexity of
actual implementation.
• Encapsulation: to hide code inside the module so that code is hidden and can be swapped or
upgraded.
• Reusability: to avoid writing the same code over and over again
• Dependency management: to easily change dependencies without rewriting our code
• Define their own Scope: Not-Polluting the Global Scope (in JavaScript)
Why do we need Modules
4. Production
Incidents
• JavaScript doesn’t come with an out-of-the-box way to modularize code, except for,
distributing code over different files.
• If we wanted to call a function in some other file, we need to explicitly load that file via script
tags before you call the function.
• If you tried to use code that you forgot to add via a script tag, then JavaScript would
complain.
• Developers should make sure that the dependencies are satisfied at the point of execution of
any block of code.
ECMAScript 5 and earlier versions of JavaScript don’t have modules.
Over time, developers came up with different patterns to simulate modular design in JavaScript.
ES6 finally introduced a standard way of defining modules.
Modules in JavaScript
5. Production
Incidents
Scripts Modules
Default mode Non-strict Strict
Top level variables are Global Local to module
Value of this at top level Window Undefined
Executed Synchronously Asynchronously
Declarative imports
(import statement)
No Yes
File extension .js .js
Scripts vs Modules
6. Production
Incidents
Two simple and widely popular workaround of Modules with JavaScript are:
– IIFE
– Revealing Module Pattern
• Both of them doesn’t satisfy all the goals of the modular programming. They cover few of them.
• They don’t require any additional software/process for loading & executing JavaScript code
• No way to programmatically import modules
• Dependencies need to be handled manually.
• Asynchronous loading of modules is not possible.
• Circular dependencies can be troublesome.
Modules in JavaScript
7. Production
Incidents
Immediately Invoked Function Expressions allow us to:
• encapsulate code complexity inside IIFE so we don't have to understand what the IIFE code does
• define variables inside the IIFE so they don't pollute the global scope
• they don't provide a mechanism for dependency management.
(function(){
console.log('test');
})();
Modules - IIFE
8. Production
Incidents
In Revealing Module pattern, modules are defined as functions that encapsulate private members
and methods, and which expose public methods.
var myModule = function(){
var message = "Hello, Welcome ";
function printMessage(myName){
console.log(message+myName);
}
return {
wishUser:printMessage
}
};
var myUser = new myModule();
myUser.wishUser();
Modules – Revealing Module Pattern
10. Production
Incidents
A module format is the syntax used to define a module.
Module Format defines the
• Identifier - name or location of the module
• Include the dependencies
• Export the API of this module
This metadata is usually defined in a configuration file.
Modules – Module Format
11. Production
Incidents
Since Module has its own format/structure, it requires software that can read and understand
the format and accordingly performs necessary actions.
A module loader at runtime:
– load the module loader(itself)
– load the root module, which is usually entry point.
– resolve dependencies and inject the dependent modules
– can asynchronously downloads the required modules in the right order and binds them
together.
– register the module
– Performs bootstrap process, that initialize all variables, their specific special methods,
and injects dependencies.
– The bootstrap also establishes a formal entry point for the module, and should be in
charge of initializing the module and kicking off the entire process.
– loader hooks are called at various points in the process of loading a module
Modules – Module Loader
12. Production
Incidents
Some of the most widely adapted and well known formats are:
– Asynchronous Module Definition (AMD)
– CommonJS
– Universal Module Definition (UMD)
– System.register
– ES6 module format
Module formats
13. Production
Incidents
// myModule
define(myModule, [], function (lib) {
// behavior for our module
function foo() {
console.log( "hello world!" );
}
// export (expose) foo to other modules as foobar
return { foobar: foo }
});
Somewhere else the module can be used with:
define(["myModule"], function(myModule) {
myModule.foobar();
});
define(
module_id /*optional*/,
[dependencies] /*optional*/,
definition function /*function for instantiating the module or object*/
);
Name of the module
List of dependencies(provided in an array)
Sample Code
Asynchronous Module Definition (AMD Format)
14. Production
Incidents
function foo() {
console.log( "hello world!" );
}
// export (expose) foo to other modules as foobar
module.exports.foobar = foo;
Somewhere else the module can be used with:
Var myModule = (‘./myModule.js’);
myModule.foobar();
var <requiremod> = require (‘<module>’);
definition function /*function for instantiating the module or object*/
module.exports.<functionName> = functionName;
Method name exposed to be used by others
Include dependent module
Sample Code
CommonJS format
15. Production
Incidents
Some of the most widely adapted and well known loaders are:
– SystemJS
– RequireJS
– StealJS
Module Loaders
Module
Format
SystemJS RequireJS StealJS
AMD Yes Yes Yes
CommonJS Yes No Yes
ES6 Yes No Yes
Global Yes No Yes
16. Production
Incidents
SystemJS is a universal module loader. It can load modules synchronously or asynchronously.
SystemJS also caches the loaded modules so if at any point in that process one of the modules has been loaded
previously then SystemJS will use the cached version instead of performing additional load and instantiation
steps.
SystemJS uses static analysis to examine the code for dependencies. It first determines the module format
used, then the modules included, and loads the modules accordingly.
Module Format Supported:
– esm: ECMAScript Module
– cjs: CommonJS
– AMD: Asynchronous Module Definition
– global: Global shim module format
– register: SystemJS’s internal module format
Module Loader - SystemJS
17. Production
Incidents
SystemJS – Configuration parameters
baseURL
• The baseURL provides a mechanism for knowing where to load plain modules names from, regardless of which parent
module they are being loaded from.
For example:
SystemJS.config({
baseURL: '/modules'
});
System.import('x');
will load x from /modules/x.
bundles
• Bundles allow a collection of modules to be downloaded together as a package whenever any module from that
collection is requested. Useful for splitting an application into sub-modules for production.
SystemJS.config({
bundles: { bundleA: ['dependencyA', 'dependencyB']
}
});
In the above any require to dependencyA or dependencyB will first trigger a SystemJS.import('bundleA') before
proceeding with the load of dependencyA or dependencyB.
depCache
• When a module specified in depCache is loaded, asynchronous loading of its pre-cached dependency list begins in
parallel.
Module Loader - SystemJS
18. Production
Incidents
SystemJS – Configuration parameters
map
• allows you to map a module alias to a location or package:
map: {
'local/package': {
x: 'vendor/x.js'
},
'another/package': {
x: 'vendor/y.js'
}
}
Means that import "x" within the file local/package/index.js will load from vendor/x.js, while the same
import in another/package/file.js will load vendor/y.js
map: {
"@angular/": "../../vendor/angular2/2.4.9-tsc/node_modules/@angular/",
"rxjs/": "../../vendor/angular2/2.4.9-tsc/node_modules/rxjs/"
}
Module Loader - SystemJS
19. Production
Incidents
SystemJS – Configuration parameters
meta
• Module meta provides an API for SystemJS to understand how to load modules correctly.
• Meta is how we set the module format of a module, or know how to shim dependencies of a global script.
Module Loader - SystemJS
meta: {
// meaning [baseURL]/vendor/angular.js when no other rules are present
// path is normalized using map and paths configuration
'vendor/angular.js': {
format: 'global', // load this module as a global
exports: 'angular', // the global property to take as the module value
deps: [ // dependencies to load before this module
'jquery' ]
}
}
Options for meta:
authorization
crossOrigin
deps
esModule
exports
format
globals
integrity
loader
nonce
sourceMap
scriptLoad
20. Production
Incidents
SystemJS – Configuration parameters
packages
• Packages provide a convenience for setting meta and map configuration that is specific to a common path.
defaultExtension
format
main
map
meta
Module Loader - SystemJS
packages: {
// meaning [baseURL]/local/package when no other rules are present
// path is normalized using map and paths configuration
'local/package': {
main: 'index.js',
format: 'cjs',
defaultExtension: 'js',
map: {
// use local jquery for all jquery requires in this package
'jquery': './vendor/local-jquery.js',
/* import '/local/package/custom-import' should route to
‘/local/package/local/import/file.js' */
'./custom-import': './local/import/file.js'
},
meta: {
// sets meta for modules within the package
'vendor/*': { 'format': 'global' }
}
}
}
21. Production
Incidents
SystemJS – Configuration parameters
packageConfigPaths
• Instead of providing package configuration information in the packages argument for System.config(), this option
allows specifying where a config file can be loaded to get the configuration for a package.
SystemJS.config({
packageConfigPaths: [
'packages/*.json',
'packages/abc/*/package.json',
'packages/abc/def/*/config.json'
]
});
paths
• Paths allow creating mappings that apply after map configuration.
• paths is similar to map, but acts as the final step in the normalization process.
• A token is first processed by map. If, after this step, it is preluded by path information, it's getting normalized (i.e.
transformed into an absolute URL, considering baseURL).
• Finally, if the token is not an absolute URL yet, it is getting matched against paths.
• It is usually advisable to use map configuration over paths unless you need strict control +over normalized
module names.
SystemJS.config({
paths: {
'app/': 'https://code.mycdn.com/app-1.2.3/'
}
});
Module Loader - SystemJS
22. Production
Incidents
SystemJS – Configuration parameters
pluginFirst
• Plugins may be loaded via plugin syntax some/file.txt!text
• Setting the pluginFirst property to true makes SystemJS follow the AMD-style plugin rules.
transpiler
• Sets the module name of the transpiler plugin to be used for loading ES6 modules.
• Represents a module name for SystemJS.import that must resolve to a valid plugin that supports transpilation of
ES modules.
warnings
• Enables the output of warnings to the console, including deprecation messages.
wasm
• When enabled, and in a browser that supports WebAssembly, all module loads will first be checked
for Web Assembly binary headers and executed as WebAssembly in browsers if so.
Module Loader - SystemJS
23. Production
Incidents
SystemJS – API
SystemJS.config
• SystemJS configuration helper function.
SystemJS.constructor
• This represents the System base class, which can be extended or reinstantiated to create a custom System instance.
SystemJS.getConfig
• Returns a clone of the internal SystemJS configuration in use.
SystemJS.import
• Loads a module by name taking an optional normalized parent name argument.
• Promise resolves to the ES module namespace value.
SystemJS.import(moduleName [, normalizedParentName]) -> Promise(Module)
SystemJS.isModule
• Given any object, returns true if the object is either a SystemJS module or native JavaScript module object, and false
otherwise.
Module Loader - SystemJS
24. Production
Incidents
SystemJS – API
SystemJS.newModule
• Given a plain JavaScript object, return an equivalent Module object.
• Useful when writing a custom instantiate hook or using SystemJS.registry.set.
SystemJS.register
• Declaration function for defining modules of the System.register polyfill module format.
SystemJS.registerDynamic
• Companion module format to System.register for non-ES6 modules.
• Provides a <script>-injection-compatible module format that any CommonJS or Global module can be converted
into for CSP compatibility.
SystemJS.resolve
• Companion module format to System.register for non-ES6 modules.
SystemJS.resolveSync
• Synchronous alternative to SystemJS.resolve.
Module Loader - SystemJS
25. Production
Incidents
SystemJS – API
SystemJS.registry
• SystemJS registry object supporting:
– SystemJS.registry.set(resolvedKey, namespace): Set a module namespace into the registry.
– SystemJS.registry.get(resolvedKey): Get a module namespace (if any) from the registry.
– SystemJS.registry.has(resolvedKey): Boolean indicating whether the given key is present in the registry.
– SystemJS.registry.delete(resolvedKey): Removes the given module from the registry (if any), returning true or
false.
– SystemJS.registry.keys: Function returning the keys iterator for the registry.
– SystemJS.registry.values: Function returning the values iterator for the registry.
– SystemJS.registry.entries: Function returning the entries iterator for the registry (keys and values).
– SystemJS.registry[Symbol.iterator]: In supported environments, provides registry entries iteration.
Module Loader - SystemJS
26. Production
Incidents
Most commonly used aspects of using SystemJS loader:
– Load the SystemJS Loader
– Set the configuration for the loader
– Import the startup module
Simple Example:
<script src="system.js">
</script>
<script>
SystemJS.config({
meta: {
format: "cjs" //use commonjs module format
}
});
SystemJS.import('game.js');
</script>
Module Loader - SystemJS
27. Production
Incidents
SystemJS.config({
packages: {
// meaning [baseURL]/local/package when no other rules are present
// path is normalized using map and paths configuration
'local/package': {
main: 'index.js',
format: 'cjs',
defaultExtension: 'js',
map: {
// use local jquery for all jquery requires in this package
'jquery': './vendor/local-jquery.js',
/* import '/local/package/custom-import‘ should route to
/local/package/local/import/file.js' */
'./custom-import': './local/import/file.js'
},
meta: {
// sets meta for modules within the package
'vendor/*': {
'format': 'global'
}
}
}
}
});
Module Loader - SystemJS
Configuration
Parameters
Example
28. Production
Incidents
• RequireJS loads plain JavaScript files as well as more defined modules.
• It implements the Asynchronous Module API.
• Each dependent module will start loading through asynchronous requests in the given order.
• Even though the file order is considered, it cannot guarantee that the first file is loaded before the second
file due to the asynchronous nature.
Module Loader - RequireJS
29. Production
Incidents
RequireJS – Configuration parameters
baseURL
• the root path to use for all module lookups.
paths
• path mappings for module names not found directly under baseUrl.
• The path settings are assumed to be relative to baseUrl, unless the paths setting starts with a "/" or has a URL
protocol in it ("like http:").
bundles
• allows configuring multiple module IDs to be found in another script.
• This only sets up where to find a module inside a script that has multiple define()'d modules in it.
• It does not automatically bind modules to the bundle's module ID. The bundle's module ID is just used for
locating the set of modules.
shim
• Configure the dependencies, exports, and custom initialization for older, traditional "browser globals" scripts
that do not use define() to declare the dependencies and set a module value.
Module Loader - RequireJS
30. Production
Incidents
RequireJS – Configuration parameters
packages
• configures loading modules from CommonJS packages.
nodeIdCompat
• This option only applies to treating the ".js" suffix differently.
waitSeconds
• The number of seconds to wait before giving up on loading a script.
context
• This allows require.js to load multiple versions of modules in a page, as long as each top-level require call
specifies a unique context string.
deps
• An array of dependencies to load.
callback
• An array of dependencies to load.
Module Loader - RequireJS
31. Production
Incidents
RequireJS – Configuration parameters
enforceDefine
• If set to true, an error will be thrown if a script loads that does not call define() or have a shim exports string
value that can be checked.
xhtml
• If set to true, document.createElementNS() will be used to create script elements.
urlArgs
• Extra query string arguments appended to URLs that RequireJS uses to fetch resources.
scriptType
• Specify the value for the type="" attribute used for script tags inserted into the document by RequireJS. Default
is "text/javascript“
skipDataMain
• If set to true, skips the data-main attribute scanning done to start module loading.
Module Loader - RequireJS
32. Production
Incidents
RequireJS – API
RequireJS includes three main API functions:
• define – the function is used to define a module. Each module is defined with a unique module ID
which will be used by RequireJS runtime functionality.
• require – the function is used to load required dependencies.
• config – the function is used to configure the requirejs runtime functionality.
Module Loader - RequireJS
33. Production
Incidents
Example:
Module Loader - RequireJS
<script src="scripts/require.js"></script>
<script>
require.config({
baseUrl: "/this/path",
paths: {
“another": “another/v1.0"
},
waitSeconds: 15
});
require( ["some/module", "my/module", "a.js", "b.js"],
function(someModule, myModule) {
//This function will be called when all the dependencies
//listed above are loaded. Note that this function could
//be called before the page is loaded.
}
);
</script>
34. Production
Incidents
The data-main attribute in our single script tag tells Require.js to load the script located at “scripts/main.js". It
automatically appends the ".js"
Module Loader - RequireJS
Project.html file contents
<script data-main="scripts/main" src="scripts/require.js"></script>
main.js file contents
//Pass a config object to require
require.config({
"packages": ["cart", "store"]
});
require(["cart", "store", "store/util"],
function (cart, store, util) {
//use the modules as usual.
});
35. Production
Incidents
• A module bundler replaces a module loader. It generates a bundle of all code at build time
• you run the module bundler to generate a bundle file at build time (e.g. bundle.js). You load
the bundle in the browser
• If you open the network tab in your browser's developer console, you will see that only 1 file
is loaded. No module loader is needed in the browser. All code is included in the bundle.
Examples of popular module bundlers are:
• Browserify: bundler for CommonJS modules
• Webpack: bundler for AMD, CommonJS, ES6 modules
Module Bundlers