Understanding (all) “modular” JavaScript formats and tools



Good day, friends!

I present to you the translation of the article “Understanding (all) JavaScript module formats and tools” by Dixin.

When creating an application, there is often a desire to divide the code into parts, logical or functional blocks (modules). However, JavaScript initially had no module support. This has led to the emergence of various modular technologies. This article discusses all the basic concepts, templates, libraries, syntax and tools for working with modules in JavaScript.


IIFE module: JS module template


By defining a variable in JS, we define it as a global variable. This means that such a variable will be available in all JS files loaded on the current page:

    //   
    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    //   
    increase()
    reset()

In order to avoid pollution of the global namespace, you can use an anonymous function:

    (() => {
        let count = 0
        // ...
    })

Voila, there are no more global variables. However, the code inside the function is not executed.

IIFE: immediate function expression


In order to execute code inside a function f, it must be called using ()how f(). To execute code inside an anonymous function (() => {})should also be used (). It looks like this (() => {})():

    (() => {
        let count = 0
        // ...
    })()

This is called IIFE (immediately called function expression). A module can be defined as follows:

    //  IIFE 
    const iifeCounterModule = (() => {
        let count = 0
        return {
            increase: () => ++count,
            reset: () => {
                count = 0
                console.log(' .')
            }
        }
    })()

    //  IIFE 
    iifeCounterModule.increase()
    iifeCounterModule.reset()

We wrap the module code in IIFE. An anonymous function returns an object. This replaces the export interface. There is only one global variable - the name of the module (or its namespace). Subsequently, the name of the module can be used to call it (export). This is called the JS module template.

Impurities of import


When defining a module, some dependencies may be required. When using a modular template, each dependent module is a global variable. Dependent modules can be defined inside an anonymous function or passed to it as arguments:

    //  IIFE   
    const iifeCounterModule = ((dependencyModule1, dependencyModule2) => {
        let count = 0
        return {
            increase: () => ++count,
            reset: () => {
                count = 0
                console.log(' .')
            }
        }
    })(dependencyModule1, dependencyModule2)

Earlier versions of popular libraries such as jQuery used this template (the latest version of jQuery uses the UMD module).

Open module: open JS module template


The open module template was coined by Christian Heilmann. This template is also IIFE, but the emphasis is on defining all interfaces as local variables inside an anonymous function:

    //   
    const revealingCounterModule = (() => {
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })()

    //   
    revealingCounterModule.increase()
    revealingCounterModule.reset()

This syntax makes it easier to understand what each interface is responsible for (or what it does).

CJS module: CommonJS module or Node.js module


CommonJS, originally named ServerJS, is a template for defining and using modules. It is built into Node.js. By default, each JS file is a CJS. Variables modulealso exportsprovide export of the module (file). The function requireprovides loading and using the module. The following code demonstrates the definition of a counter module in CommonJS syntax:

    //  CommonJS : commonJSCounterModule.js
    const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')

    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    exports.increase = increase
    exports.reset = reset
    //  ()
    module.exports = {
        increase,
        reset
    }

Here's how this module is used:

    //  CommonJS 
    const {
        increase,
        reset
    } = require('./commonJSCounterModule')
    increase()
    reset()
    // 
    const commonJSCounterModule = require('./commonJSCounterModule')
    commonJSCounterModule.increase()
    commonJSCounterModule.reset()

In the Node.js runtime (engine), this template is used by wrapping the code inside the file into a function, to which variables exports, moduleand a function are passed as parameters require:

    //  CommonJS 
    (function(exports, require, module, __filename, __dirname) {
        const dependencyModule1 = require('./dependencyModule1')
        const dependencyModule2 = require('./dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        module.exports = {
            increase,
            reset
        }

        return module.exports
    }).call(thisValue, exports, require, module, filename, dirname)

    //  CommonJS 
    (function(exports, require, module, __filename, __dirname) {
        const commonJSCounterModule = require('./commonJSCounterModule')
        commonJSCounterModule.increase()
        commonJSCounterModule.reset()
    }).call(thisValue, exports, require, module, filename, dirname)

AMD module or RequireJS module


AMD ( asynchronous module definition ) is a template for defining and using modules. It is used in the RequireJS library . AMD contains a definemodule definition function that accepts the module name, dependency names, and factory function:

    //  AMD 
    define('amdCounterModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })

It also contains a function requirefor using the module:

    //  AMD 
    require(['amdCounterModule'], amdCounterModule => {
        amdCounterModule.increase()
        amdCounterModule.reset()
    })


requireAMD differs from requireCommonJS in that it takes the names of the modules and the modules themselves as arguments to the function.

Dynamic loading


The function definealso has a different purpose. It takes a callback function and passes a CommonJS-like requirefunction to this function. Inside the callback function, require is called to dynamically load the module:

    //   AMD 
    define(require => {
        const dynamicDependencyModule1 = require('dependencyModule1')
        const dynamicDependencyModule2 = require('dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })

AMD module from CommonJS module


The above function define, in addition require, can take the variables exportsand as arguments module. Therefore, definecode from CommonJS can be executed inside :

    //  AMD   CommonJS 
    define((require, exports, module) => {
        // CommonJS 
        const dependencyModule1 = require('dependencyModule1')
        const dependencyModule2 = require('dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        exports.increase = increase
        exports.reset = reset
    })

    //  AMD   CommonJS 
    define(require => {
        // CommonJS 
        const counterModule = require('amdCounterModule')
        counterModule.increase()
        counterModule.reset()
    })

UMD module: universal module definition or UmdJS module


UMD ( universal module definition ) - a set of templates for ensuring the operation of the module in different runtimes

UMD for AMD (RequireJS) and browser


The following code provides the module in both AMD (RequireJS) and the browser:

    //  UMD   AMD (RequireJS)  
    ((root, factory) => {
        //   define AMD/RequireJS
        if (typeof define === 'function' && define.amd) {
            //  ,      
            define('umdCounterModule', ['dependencyModule1', 'dependencyModule2'], factory)
        } else {
            //  ,    
            //      (  Window)
            //    
            root.umdCounterModule = factory(root.dependencyModule1, root.dependencyModule2)
        }
    })(typeof self !== undefined ? self : this, (dependencyModule1, dependencyModule2) => {
        //  
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' ')
        }

        return {
            increase,
            reset
        }
    })

It looks complicated, but it's just IIFE. An anonymous function determines if there is a function definefrom AMD / RequireJS.

  • If definedetected, the factory function is called through it.
  • If definenot found, the factory function is called directly. At this point, the argument rootis the browser Window object. It receives dependent modules from global variables (properties of the Window object). When a factorymodule returns, it also becomes a global variable (a property of the Window object).

UMD for AMD (RequireJS) and CommonJS (Node.js)


The following code provides the module in both AMD (RequireJS) and CommonJS (Node.js):

    (define => define((require, exports, module) => {
        //  
        const dependencyModule1 = require("dependencyModule1")
        const dependencyModule2 = require("dependencyModule2")

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log("Count is reset.")
        }

        module.export = {
            increase,
            reset
        }
    }))( //   module  exports  CommonJS/Node.js
        //    define  AMD/RequireJS
        typeof module === 'object' && module.exports && typeof define !== 'function'
            ? // CommonJS/Node.js.    define
                factory => module.exports = factory(require, exports, module)
            : // AMD/RequireJS.   define
                define)

Don’t be scared, it’s just IIFE again. When an anonymous function is called, its argument is “evaluated”. Assessing the argument to determine the execution environment (defined by the presence of variables moduleand exportsof CommonJS / Node.js, and the functions defineof the AMD / RequireJS).

  • If the runtime is CommonJS / Node.js, the anonymous function argument manually creates the function define.
  • If the runtime is AMD / RequireJS, the argument to the anonymous function is a function definefrom that environment. Performing an anonymous function ensures that the function works define. Inside an anonymous function, a function is called to create the module define.

ES module: ECMAScript2015 or ES6 module


In 2015, version 6 of the JS specification introduced a new modular syntax. This is called ECMAScript 2015 (ES2015) or ECMAScript 6 (ES6). The basis of the new syntax is the keywords importand export. The following code demonstrates the use of the ES module for named and default (default) import / export:

    //  ES : esCounterModule.js  esCounterModule.mjs
    import dependencyModule1 from './dependencyModule1.mjs'
    import dependencyModule2 from './dependencyModule2.mjs'

    let count = 0
    //  
    export const increase = () => ++count
    export const reset = () => {
        count = 0
        console.log(' .')
    }
    //    
    export default {
        increase,
        reset
    }

To use the module file in the browser, add the tag <script>and identify it as a module: <script type="module" src="esCounterModule.js"></script>. To use this module in Node.js, change its extension to .mjs:

    //  ES 
    //    
    import {
        increase,
        reset
    } from './esCounterModule.mjs'
    increase()
    reset()
    //      
    import esCounterModule from './esCounterModule.mjs'
    esCounterModule.increase()
    esCounterModule.reset()


For backward compatibility in the browser, you can add a tag <script>with the attribute nomodule:

    <script nomodule>
        alert ('Not supported.')
    </script>

ES dynamic module: ECMAScript2020 or ES11 dynamic module


The latest 11 version of the JS 2020 specification introduces a built-in function importfor the dynamic use of ES modules. This function returns a promise, so you can use the module with then:

    //   ES    ,    
    import('./esCounterModule.js').then(({
        increase,
        reset
    }) => {
        increase()
        reset()
    })
    //      
    import('./esCounterModule.js').then(dynamicESCounterModule => {
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

Due to the fact that the function importreturns a promise, it can use the keyword await:

    //   ES   async/await
    (async () => {
        //    
        const {
            increase,
            reset
        } = await import('./esCounterModule.js')
        increase()
        reset()

        //      
        const dynamicESCounterModule = await import('./esCounterModule.js')
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

System Module: SystemJS Module


SystemJS is a library for supporting ES modules in older browsers. For example, the following module is written using ES6 syntax:

    //  ES 
    import dependencyModule1 from "./dependencyModule1.js"
    import dependencyModule2 from "./dependencyModule2.js"
    dependencyModule1.api1()
    dependencyModule2.api2()

    let count = 0
    //  
    export const increase = function() {
        return ++count
    }
    export const reset = function() {
        count = 0
        console.log("Count is reset.")
    }
    //    
    export default {
        increase,
        reset
    }

This code will not work in browsers that do not support ES6 syntax. One solution to this problem is to translate the code using the System.registerSystemJS library interface:

    //  SystemJS 
    System.register(['./dependencyModule1', './dependencyModule2'], function(exports_1, context_1) {
        'use strict'
        var dependencyModule1_js_1, dependencyModule2_js_1, count, increase, reset
        var __moduleName = context_1 && context_1.id
        return {
            setters: [
                function(dependencyModule1_js_1_1) {
                    dependencyModule1_js_1 = dependencyModule1_js_1_1
                },
                function(dependencyModule2_js_1_1) {
                    dependencyModule2_js_1 = dependencyModule2_js_1_1
                }
            ],
            execute: function() {
                dependencyModule1_js_1.default.api1()
                dependencyModule2_js_1.default.api2()
                count = 0
                //  
                exports_1('increase', increase = function() {
                    return ++count
                })
                exports_1('reset', reset = function() {
                    count = 0
                    console.log(' .')
                })
                //    
                exports_1('default', {
                    increase,
                    reset
                })
            }
        }
    })

The new modular ES6 syntax is gone. But the code will work fine in older browsers. This transpilation can be done automatically using Webpack, TypeScript, etc.

Dynamic module loading


SystemJS also contains a function importfor dynamic import:

    //  SystemJS   
    System.import('./esCounterModule.js').then(dynamicESCounterModule => {
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

Webpack module: compilation and assembly of CJS, AMD and ES modules


Webpack is a module builder. Its transpiler combines CommonJS, AMD and ES modules into a single balanced modular template and collects all the code into a single file. For example, in the following 3 files, 3 modules are defined using different syntax:

    //  AMD : amdDependencyModule1.js
    define('amdDependencyModule1', () => {
        const api1 = () => {}
        return {
            api1
        }
    })

    //  CommonJS : commonJSDependencyModule2.js
    const dependencyModule2 = require('./commonJSDependencyModule2')
    const api2 = () => dependencyModule1.api1()
    exports.api2 = api2

    //  ES : esCounterModule.js
    import dependencyModule1 from './amdDependencyModule1'
    import dependencyModule2 from './commonJSDependencyModule2'

    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    export default {
        increase,
        reset
    }

The following code demonstrates the use of this module:

    //  ES : index.js
    import counterModule from './esCounterModule'
    counterModule.increase()
    counterModule.reset()

Webpack is able to combine these files, despite the fact that they are different modular systems, into one file main.js:

    root
        dist
            main.js (assembly of files located in the src folder)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        webpack.config.js

Since Webpack is based on Node.js, it uses the modular syntax of CommonJS. In webpack.config.js:

    const path = require('path')

    module.exports = {
        entry: './src/index.js',
        mode: 'none', //          
        output: {
            filename: 'main.js',
            path: path.resolve(__dirname, 'dist'),
        },
    }

To compile and build, you must run the following commands:

    npm install webpack webpack-cli --save-dev
    npx webpack --config webpack.config.js

As a result, Webpack will create the file main.js. The following code is main.jsformatted to improve readability:

    (function(modules) { //  Webpack
        //    
        var installedModules = {}
        //  require
        function require(moduleId) {
            // ,     
            if (installedModules[moduleId]) {
                return installedModules[moduleId].exports
            }
            //        
            var module = installedModules[moduleId] = {
                i: moduleId,
                l: false,
                exports: {}
            }
            //   module
            modules[moduleId].call(module.exports, module, module.exports, require)
            //    
            module.l = true
            //   exports 
            return module.exports
        }

        //   modules (__webpack_modules__)
        require.m = modules
        //    
        require.c = installedModules
        //     
        require.d = function(exports, name, getter) {
            if (!require.o(exports, name)) {
                Object.defineProperty(exports, name, {
                    enumerable: true,
                    get: getter
                })
            }
        }
        //  __esModule  exports
        require.r = function(exports) {
            if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: 'Module'
                })
            }
            Object.defineProperty(exports, '__esModule', {
                value: true
            })
        }
        //       
        // mode & 1:  -  ,  
        // mode & 2:       ns (namespace)
        // mode & 4:  ,   ns  
        // mode & 8|1:   require
        require.t = function(value, mode) {
            if (mode & 1) value = require(value)
            if (mode & 8) return value
            if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value
            var ns = Object.create(null)
            require.r(ns)
            Object.defineProperty(ns, 'default', {
                enumerable: true,
                value: value
            })
            if (mode & 2 && typeof value !== 'string')
                for (var key in value) require.d(ns, key, function(key) {
                    return value[key]
                }.bind(null, key))
            return ns
        }
        //  getDefaultExport      
        require.n = function(module) {
            var getter = module && module.__esModule ?
                function getDefault() {
                    return module['default']
                } :
                function getModuleExports() {
                    return module
                }
            require.d(getter, 'a', getter)
            return getter
        }
        // Object.prototype.hasOwnProperty.call
        require.o = function(object, property) {
            return Object.prototype.hasOwnProperty.call(object, property)
        }
        // __webpack_public_path__
        require.p = ''
        //      exports
        return require(require.s = 0)
    })([
        function(module, exports, require) {
            'use strict'
            require.r(exports)
            //  ES : index.js
            var esCounterModule = require(1)
            esCounterModule['default'].increase()
            esCounterModule['default'].reset()
        },
        function(module, exports, require) {
            'use strict'
            require.r(exports)
            //  ES : esCounterModule.js
            var amdDependencyModule1 = require.n(require(2))
            var commonJSDependencyModule2 = require.n(require(3))
            amdDependencyModule1.a.api1()
            commonJSDependencyModule2.a.api2()

            let count = 0
            const increase = () => ++count
            const reset = () => {
                count = 0
                console.log(' .')
            }

            exports['default'] = {
                increase,
                reset
            }
        },
        function(module, exports, require) {
            var result!(result = (() => {
                    //  AMD : amdDependencyModule1.js
                    const api1 = () => {}
                    return {
                        api1
                    }
                }).call(exports, require, exports, module),
                result !== undefined && (module.exports = result))
        },
        function(module, exports, require) {
            //  CommonJS : commonJSDependencyModule2.js
            const dependencyModule1 = require(2)
            const api2 = () => dependencyModule1.api1()
            exports.api2 = api2
        }
    ])

And again, this is just IIFE. The code from 4 files is converted into an array of 4 functions. And this array is passed to the anonymous function as a parameter.

Babel module: transpilation of ES module


Babel is another transporter for supporting ES6 + code in older browsers. The above ES6 + module can be converted to a Babel module as follows:

    // Babel
    Object.defineProperty(exports, '__esModule', {
        value: true
    })
    exports['default'] = void 0

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

    //  ES : esCounterModule.js
    var dependencyModule1 = _interopRequireDefault(require('./amdDependencyModule1'))
    var dependencyModule2 = _interopRequireDefault(require('./commonJSDependencyModule2'))
    dependencyModule1['default'].api1()
    dependencyModule2['default'].api2()

    var count = 0
    var increase = function() {
        return ++count
    }
    var reset = function() {
        count = 0
        console.log(' .')
    }

    exports['default'] = {
        increase: increase,
        reset: reset
    }

And here is the code in index.js, demonstrating the use of this module:

    // Babel
    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            'default': obj
        }
    }

    //  ES : index.js
    var esCounterModule = _interopRequireDefault(require('./esCounterModule.js'))
    esCounterModule['default'].increase()
    esCounterModule['default'].reset()

This is the default transpilation. Babel also knows how to work with other tools.

Babel and SystemJS


SystemJS can be used as a plugin for Babel:

    npm install --save-dev @ babel / plugin-transform-modules-systemjs


This plugin should be added to babel.config.json:

    {
        'plugins': ['@ babel / plugin-transform-modules-systemjs'],
        'presets': [
            [
            '@ babel / env',
                {
                    'targets': {
                        'ie': '11'
                    }
                }
            ]
        ]
    }

Now Babel can work with SystemJS to transpile CommonJS / Node.js, AMD / RequireJS and ES modules:

    npx babel src --out-dir lib

Result:

    root
        lib
            amdDependencyModule1.js (transpiled using SystemJS)
            commonJSDependencyModule2.js (transpiled using SystemJS)
            esCounterModule.js (transpiled using SystemJS)
            index.js (transpiled using SystemJS)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        babel.config.json

The entire syntax of AMD, CommonJS and ES modules is transposed into SystemJS syntax:

    //  AMD/RequireJS   SystemJS : lib/amdDependencyModule1.js
    System.register([], function(_export, _context) {
        'use strict'
        return {
            setters: [],
            execute: function() {
                //  AMD : src/amdDependencyModule1.js
                define('amdDependencyModule1', () => {
                    const api1 = () => {}

                    return {
                        api1
                    }
                })
            }
        }
    })

    //  CommonJS/Node.js   SystemJS : lib/commonJSDependencyModule2.js.
    System.register([], function(_export, _context) {
        'use strict'
        var dependencyModule1, api2
        return {
            setters: [],
            execute: function() {
                //  CommonJS : src/commonJSDependencyModule2.js
                dependencyModule1 = require('./amdDependencyModule1')

                api2 = () => dependencyModule1.api1()

                exports.api2 = api2
            }
        }
    })

    //  ES   SystemJS 
    System.register(['./amdDependencyModule1', './commonJSDependencyModule2'], function(_export, _context) {
        var dependencyModule1, dependencyModule2, count, increase, reset
        return {
            setters: [function(_amdDependencyModule) {
                dependencyModule1 = _amdDependencyModule.default
            }, function(_commonJSDependencyModule) {
                dependencyModule2 = _commonJSDependencyModule.default
            }],
            execute: function() {
                //  ES : src/esCounterModule.js
                dependencyModule1.api1()
                dependencyModule1.api2()
                count = 0

                increase = () => ++count

                reset = () => {
                    count = 0
                    console.log(' .')
                }

                _export('default', {
                    increase,
                    reset
                })
            }
        }
    })

    //  ES   SystemJS : lib/index.js
    System.register(['./esCounterModule'], function(_export, _context) {
        var esCounterModule
        return {
            setters: [function(_esCounterModule) {
                esCounterModule = _esCounterModule.default
            }],
            execute: function() {
                //  ES : src/index.js
                esCounterModule.increase()
                esCounterModule.reset()
            }
        }
    })

TypeScript module: transpilation of CJS, AMD, ES and SystemJS modules


TypeScript supports all flavors of JS syntax, including ES6. During transpilation, the syntax of the ES6 module can be saved or converted to another format, including CommonJS / Node.js, AMD / RequireJS, UMD / UmdJS or SystemJS according to the settings of the transpilation in tsconfig.json:

    {
        'compilerOptions': {
            'module': 'ES2020' // None, CommonJS, AMD, System,
            UMD, ES6, ES2015, ESNext
        }
    }

For instance:

    // TypeScript  ES 
    //  compilerOptions: { module: 'ES6' }.    
    import dependencyModule from './dependencyModule'
    dependencyModule.api()
    let count = 0
    export const increase = function() {
        return ++count
    }

    //  compilerOptions: { module: 'CommonJS' }.   CommonJS/Node.js 
    var __importDefault = (this && this.__importDefault) || function(mod) {
        return (mod && mod.__esModule) ? mod : {
            'default': mod
        }
    }
    exports.__esModule = true

    var dependencyModule_1 = __importDefault(require('./dependencyModule'))
    dependencyModule_1['default'].api()
    var count = 0
    exports.increase = function() {
        return ++count
    }

    //  compilerOptions: { module: 'AMD' }.   AMD/RequireJS 
    var __importDefault = (this && this.__importDefault) || function(mod) {
        return (mod && mod.__esModule) ? mod : {
            'default': mod
        }
    }
    define(['require', 'exports', './dependencyModule'], function(require, exports, dependencyModule_1) {
        'use strict'
        exports.__esModule = true

        dependencyModule_1 = __importDefault(dependencyModule_1)
        dependencyModule_1['default'].api()
        var count = 0
        exports.increase = function() {
            return ++count
        }
    })

    //  compilerOptions: { module: 'UMD' }.   UMD/UmdJS 
    var __importDefault = (this & this.__importDefault) || function(mod) {
            return (mod && mod.__esModule) ? mod : {
                'default': mod
            }
        }
        (function(factory) {
            if (typeof module === 'object' && typeof module.exports === 'object') {
                var v = factory(require, exports)
                if (v !== undefined) module.exports = v
            } else if (typeof define === 'function' && define.amd) {
                define(['require', 'exports', './dependencyModule'], factory)
            }
        })(function(require, exports) {
            'use strict'
            exports.__esModule = true

            var dependencyModule_1 = __importDefault(require('./dependencyModule'))
            dependencyModule_1['default'].api()
            var count = 0
            exports.increase = function() {
                return ++count
            }
        })

    //  compilerOptions: { module: 'System' }.   System/SystemJS 
    System.register(['./dependencyModule'], function(exports_1, context_1) {
        'use strict'
        var dependencyModule_1, count, increase
        car __moduleName = context_1 && context_1.id
        return {
            setters: [
                function(dependencyModule_1_1) {
                    dependencyModule_1 = dependencyModule_1_1
                }
            ],
            execute: function() {
                dependencyModule_1['default'].api()
                count = 0
                exports_1('increase', increase = function() {
                    return ++count
                })
            }
        }
    })

The modular ES syntax supported by TypeScript is called external modules.

Internal Modules and Namespace


TypeScript also has keywords moduleand namespace. They are called internal modules:

    module Counter {
        let count = 0
        export const increase = () => ++count
        export const reset = () => {
            count = 0
            console.log(' .')
        }
    }

    namespace Counter {
        let count = 0
        export const increase = () => ++count
        export const reset = () => {
            count = 0
            console.log(' .')
        }
    }

Both are transposed into JS objects:

    var Counter;
    (function(Counter) {
        var count = 0
        Counter.increase = function() {
            return ++count
        }
        Counter.reset = function() => {
            count = 0
            console.log(' .')
        }
    })(Counter || (Counter = {}))

TypeScript module and namespace can have several levels of nesting through the separator .:

    module Counter.Sub {
        let count = 0
        export const increase = () => ++count
    }

    namespace Counter.Sub {
        let count = 0
        export const increase = () => ++count
    }

Sub module and sub namespace are transposed into object properties:

    var Counter;
    (function(Counter) {
        var Sub;
        (function(Sub) {
            var count = 0
            Sub.increase = function() {
                return ++count
            }
        })(Sub = Counter.Sub || (Counter.Sub = {}))
    })(Counter || (Counter = {}))

TypeScript module and namespace can also be used in a statement export:

    module Counter {
        let count = 0
        export module Sub {
            export const increase = () => ++count
        }
    }

    module Counter {
        let count = 0
        export namespace Sub {
            export const increase = () => ++count
        }
    }

The above code also translates to sub module and sub namespace:

    var Counter;
    (function(Counter) {
        var count = 0
        var Sub;
        (function(Sub) {
            Sub.increase = function() {
                return ++count
            }
        })(Sub = Counter.Sub || (Counter.Sub = {}))
    })(Counter || (Counter = {}))

Conclusion


Welcome to JS, which has 10+ systems / modulation formats / namespaces:

  • IIFE module: JS module template
  • Open module: open JS module template
  • CJS module: CommonJS module or Node.js module
  • AMD module: asynchronous module definition or RequireJS module
  • UMD module: universal module definition or UmdJS module
  • ES module: ECMAScript2015 or ES6 module
  • ES dynamic module: ECMAScript2020 or ES11 dynamic module
  • System Module: SystemJS Module
  • Webpack module: compilation and assembly of CJS, AMD and ES modules
  • Babel module: transpilation of ES module
  • TypeScript module and namespace

Fortunately, JS currently has standard built-in tools for working with modules supported by Node.js and all modern browsers. For older browsers, you can use the new modular ES syntax, translating it into compatible syntax using Webpack / Babel / SystemJS / TypeScript.

Thank you for your time. I hope it was well spent.

All Articles