{"id":7126,"date":"2020-07-11T20:52:29","date_gmt":"2020-07-11T20:52:29","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=7126"},"modified":"2020-07-11T20:52:29","modified_gmt":"2020-07-11T20:52:29","slug":"modules-modules-and-yet-more-modules","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/modules-modules-and-yet-more-modules\/","title":{"rendered":"Modules, modules and yet more modules"},"content":{"rendered":"<p><em>Another post that&#8217;s sat in draft for a while &#8211; native JavaScript module implementations are now supported in all modern browsers, but I think it&#8217;s still interesting to look at the variations in module systems.<\/em><\/p>\n<p>JavaScript doesn&#8217;t (as such) come with a built in module system. Originally it was primarily used for script elements within an HTML document (inline scripting or even scripts per file) not a full blown framework\/application as we see nowadays with the likes of React, Angular, Vue etc. <\/p>\n<p>The concept of modules was introduced as a means to enable developers to separate their code into separate files and this ofcourse aids in reuse of code, maintainability etc. This is not all that modules offer. In the early days of JavaScript you could store your scripts in separate files but these would then end up in the <em>global namespace<\/em> which is not ideal, especially if you start sharing your scripts with others (or using other&#8217;s scripts), then there&#8217;s the potential of name collisions, i.e. more than one function with the same name in the global namespace.<\/p>\n<p>If you come from a language such as C#, Java or the likes then you may find yourself being slightly surprised that this is a big deal in JavaScript, after all, these languages (and languages older than JavaScript) seemed to have solved this problems already. This is simply the way it was with JavaScript because of it&#8217;s original scripting background. <\/p>\n<p>What becomes more confusing is that there isn&#8217;t a single solution to modules and how they should work, several groups have created there own versions of modules over the life of JavaScript.<\/p>\n<p><strong>Supported by TypeScript<\/strong><\/p>\n<p>This post is not a history of JavaScript modules so we&#8217;re not going to cover every system every created but instead primarily concentrate on those supported by the TypeScript transpiler, simply because this where my interest in the different modules came from.<\/p>\n<p>TypeScript supports the following module types, &#8220;none&#8221;, &#8220;commonjs&#8221;, &#8220;amd&#8221;, &#8220;system&#8221;, &#8220;umd&#8221;, &#8220;es2015&#8221; and &#8220;ESNext&#8221;. These are the one&#8217;s that you can set in the tsconfig.json&#8217;s compilerOptions, module.<\/p>\n<p>To save me looking at writing my own examples of the JavaScript code, we&#8217;ll simply let the TypeScript transpiler generate code for us and look at how that looks\/works.<\/p>\n<p>Here&#8217;s a simple TypeScript (Demo.ts) file with some code (for simplicity we&#8217;re not going to worry about lint rules such as one class per file or the likes).<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nexport class MyClass {    \r\n}\r\n\r\nexport default class MyDefaultClass {    \r\n}\r\n<\/pre>\n<p>Now my tsconfig.json looks like this<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n{\r\n  &quot;compilerOptions&quot;: {\r\n    &quot;target&quot;: &quot;es6&quot;,  \r\n    &quot;module&quot;: &quot;commonjs&quot;,  \r\n    &quot;strict&quot;: true\r\n  },\r\n  &quot;include&quot;: &#x5B;&quot;Demo.ts&quot;]\r\n}\r\n<\/pre>\n<p>And I&#8217;ll simply change the &#8220;module&#8221; to each of the support module kinds and we&#8217;ll look at the resultant JavaScript source. Alternatively we can use this index.js<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nvar ts = require('typescript');\r\n\r\nlet code = 'export class MyClass {' + \r\n'}' +\r\n'export default class MyDefaultClass {' +\r\n'}';\r\n\r\nlet result = ts.transpile(code, { module: ts.ModuleKind.CommonJS});\r\nconsole.log(result);\r\n<\/pre>\n<p>and change the ModuleKind the review the console output. Either way we should get a chance to look at what style of output we get.<\/p>\n<p><strong>Module <em>CommonJS<\/em><\/strong><\/p>\n<p>Setting tsconfig module to CommonJS results in the following JavaScript being generated. The Object.defineProperty simple assigns the property __esModule to the exports. Other than this the class definitions are very similar to our original code. The main difference is in how the exports are created.<\/p>\n<p><em>The __esModule is set to true to let &#8220;importing&#8221; modules know that this code is is a transpiled ES module. It appears this matters specifically with regards to default exports. See <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/blob\/master\/src\/compiler\/transformers\/module\/module.ts\" rel=\"noopener noreferrer\" target=\"_blank\">module.ts<\/a> <\/em><\/p>\n<p>The exports are made directly to the <em>exports<\/em> map and available via the name\/key. For example exports[&#8216;default&#8217;] would supply the MyDefaultClass, like wise exports[&#8216;MyClass&#8217;] would return the MyClass type.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n&quot;use strict&quot;;\r\nObject.defineProperty(exports, &quot;__esModule&quot;, { value: true });\r\nclass MyClass {\r\n}\r\nexports.MyClass = MyClass;\r\nclass MyDefaultClass {\r\n}\r\nexports.default = MyDefaultClass;\r\n<\/pre>\n<p>We can import the exports using require, for example<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nvar demo = require('.\/DemoCommon');\r\n\r\nvar o = new demo.MyClass();\r\n<\/pre>\n<p><strong>Module <em>AMD<\/em><\/strong><\/p>\n<p>The Asynchronous Module Definition was designed to allow the module to be asynchronously loaded. CommonJS is a synchronous module style and hence when it&#8217;s being loaded the application will be blocked\/halted. With the AMD module style expects the <em>define<\/em> function which passes arguments into a callback function. <\/p>\n<p>As you can see the code within the define function is basically our CommonJS file wrapped in the callback. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ndefine(&#x5B;&quot;require&quot;, &quot;exports&quot;], function (require, exports) {\r\n    &quot;use strict&quot;;\r\n    Object.defineProperty(exports, &quot;__esModule&quot;, { value: true });\r\n    class MyClass {\r\n    }\r\n    exports.MyClass = MyClass;\r\n    class MyDefaultClass {\r\n    }\r\n    exports.default = MyDefaultClass;\r\n});\r\n<\/pre>\n<p>To import\/require AMD modules we need to use <a href=\"https:\/\/requirejs.org\/\" rel=\"noopener noreferrer\" target=\"_blank\">requirejs<\/a>, for example assuming we created a file from the above generated code named DemoAmd.js, then we can access the module using<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nvar requirejs = require('requirejs');\r\nrequirejs.config({\r\n    baseUrl: __dirname,\r\n    nodeRequire: require\r\n});\r\n\r\nvar demo = requirejs('.\/DemoAmd');\r\n\r\nvar o = new demo.MyClass();\r\n<\/pre>\n<p><strong>Module <em>System<\/em><\/strong><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nSystem.register(&#x5B;], function (exports_1, context_1) {\r\n    &quot;use strict&quot;;\r\n    var MyClass, MyDefaultClass;\r\n    var __moduleName = context_1 &amp;&amp; context_1.id;\r\n    return {\r\n        setters: &#x5B;],\r\n        execute: function () {\r\n            MyClass = class MyClass {\r\n            };\r\n            exports_1(&quot;MyClass&quot;, MyClass);\r\n            MyDefaultClass = class MyDefaultClass {\r\n            };\r\n            exports_1(&quot;default&quot;, MyDefaultClass);\r\n        }\r\n    };\r\n});\r\n<\/pre>\n<p><strong>Module <em>UMD<\/em><\/strong><\/p>\n<p>UMD or Universal Module Definition is a module style which can be used in place of CommonJS or AMD in that it uses if\/else to generate modules in either CommonJS or AMD style. This means you can write your modules in a way that can be imported into a system expecting either CommonJS or AMD.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n(function (factory) {\r\n    if (typeof module === &quot;object&quot; &amp;&amp; typeof module.exports === &quot;object&quot;) {\r\n        var v = factory(require, exports);\r\n        if (v !== undefined) module.exports = v;\r\n    }\r\n    else if (typeof define === &quot;function&quot; &amp;&amp; define.amd) {\r\n        define(&#x5B;&quot;require&quot;, &quot;exports&quot;], factory);\r\n    }\r\n})(function (require, exports) {\r\n    &quot;use strict&quot;;\r\n    Object.defineProperty(exports, &quot;__esModule&quot;, { value: true });\r\n    class MyClass {\r\n    }\r\n    exports.MyClass = MyClass;\r\n    class MyDefaultClass {\r\n    }\r\n    exports.default = MyDefaultClass;\r\n});\r\n<\/pre>\n<p>We can import UMD modules using either require (as shown in our CommonJS code) or requirejs (as shown in our AMD code) as UMD modules represent modules in both styles.<\/p>\n<p><strong>Module <em>es2015<\/em> and <em>ESNext<\/em><\/strong><\/p>\n<p>As you might expect as the TypeScript we&#8217;ve written is pretty standard for ES2015 the resultant code looks identical. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nexport class MyClass {\r\n}\r\nexport default class MyDefaultClass {\r\n}\r\n<\/pre>\n<p><strong>Module <em>None<\/em><\/strong><\/p>\n<p>I&#8217;ve left this one until last as it needs further investigation as module None suggests that code is created which is not part of the module system, however the code below works quite happily with <em>require<\/em> form of import. This is possibly a lack of understanding of it&#8217;s use by me. It&#8217;s included for completeness.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n&quot;use strict&quot;;\r\nexports.__esModule = true;\r\nvar MyClass = \/** @class *\/ (function () {\r\n    function MyClass() {\r\n    }\r\n    return MyClass;\r\n}());\r\nexports.MyClass = MyClass;\r\nvar MyDefaultClass = \/** @class *\/ (function () {\r\n    function MyDefaultClass() {\r\n    }\r\n    return MyDefaultClass;\r\n}());\r\nexports&#x5B;&quot;default&quot;] = MyDefaultClass;\r\n<\/pre>\n<p><strong>Inspecting modules<\/strong><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nfunction printModule(m, indent = 0) {\r\n    let indents = ' '.repeat(indent);\r\n\r\n    console.log(`${indents}Filename: ${m.filename}`);    \r\n    console.log(`${indents}Id: ${m.id}`);    \r\n    let hasParent = m.parent !== undefined &amp;&amp; m.parent !== null;\r\n    console.log(`${indents}HasParent: ${hasParent}`);\r\n    console.log(`${indents}Loaded: ${m.loaded}`);\r\n    if(m.export !== &#x5B;]) {\r\n        console.log(`${indents}Exports`);\r\n        for(const e of Object.keys(m.exports)) {\r\n            console.log(`${indents}  ${e}`);\r\n        }\r\n    }\r\n    if(m.paths !== &#x5B;]) {\r\n        console.log(`${indents}Paths`);\r\n        for(const p of m.paths) {\r\n            console.log(`${indents}  ${p}`);\r\n        }\r\n    }\r\n\r\n    if(m.children !== &#x5B;]) {\r\n        console.log(`${indents}Children`);\r\n        for (const child of m.children) {\r\n            printModule(child, indent + 3);\r\n        }\r\n    }\r\n}\r\n\r\n\r\nconsole.log(Module._nodeModulePaths(Path.dirname('')));\r\nprintModule(module);\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Another post that&#8217;s sat in draft for a while &#8211; native JavaScript module implementations are now supported in all modern browsers, but I think it&#8217;s still interesting to look at the variations in module systems. JavaScript doesn&#8217;t (as such) come with a built in module system. Originally it was primarily used for script elements within [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[45,46],"tags":[],"class_list":["post-7126","post","type-post","status-publish","format-standard","hentry","category-javascript","category-typescript"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=7126"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7126\/revisions"}],"predecessor-version":[{"id":8435,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7126\/revisions\/8435"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=7126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=7126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=7126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}