Module system vs module format - javascript

I'm approaching modules for the very first time and I'm a little bit confused.
I read from various docs that there are several modules systems, like commonjs (sync), and requirejs (AMD). From ES6 plain javascript has its own module sys, which is based on commonjs.
Then I started studying webpack, that resolves dependency using commonjs or requirejs module formats, and from here starts my confusion: as far as I understand, those two are module systems, they are designed to resolve the dependencies tree on its own; it's their purpose.
What is the sense to use the commonjs/requirejs format (aka syntax) and then implement webpack to resolve the graph?

CommonJS and AMD are both runtime module systems. Webpack is compiling your code, so you're not using it to resolve the graph specifically, you're using to create a build of an application from source files. To accomplish that, webpack 'understands' both types of modules so that it can analyze your code, identify dependencies, and bundle and optimize appropriately.
Webpack supports things that the original module systems don't: you can use more dynamic expressions to represent the module you're requiring; you can extend the require resolution behavior itself; you can specify an asynchronous runtime loading using its require.ensure syntax. Webpack's author could have defined a new module system to support these additional features, but then you'd have to rewrite all your existing source (and any third party modules) to accommodate the build tool. Instead, webpack is good about supporting whatever modules you already use, and giving you additional tools to handle more complex build needs.

Related

Is CommonJS 'require' still used or deprecated?

Current Javascript adopts import from ES6 as a standard way to import modules.
However, I sometimes see codes using CommonJS require instead of import.
I first wondered whether two can be used together, but it seems like two are not interchangeable. (releated stackoverflow question)
So, is CommonJS 'require' something that is still used in projects? Or is it slowly dying and needed only for maintaining legacy codes?
CommonJS will likely be supported in nodejs for a long time as there are still millions of lines of code written using it. It is the original module loading mechanism in nodejs. It is not deprecated.
ESM modules (ECMAScript modules) that use import and export are the new Javascript standard for modules and we can expect that nodejs will support these for as long as they are the Javascript standard (probably forever).
These two modules standards are not entirely compatible so mixing and matching within the same project can lead to complications that you have to learn how to deal with.
New Projects
If I were starting a new project today, I'd choose to write my code using ESM modules as it is the future trajectory of the language and nodejs. And, you can still load CommonJS modules into ESM modules if you require backward compatibility with other modules, but you do have to know how to do it properly - it's not always seamless to mix and match module types.
When ESM modules were first supported in nodejs, the interoperability with CommonJS modules was not very full featured and created some difficulties. As of the more recent versions of nodejs, you can load a CommonJS module from an ESM module or vice versa- you just have to use the right techniques to do it. This makes working from an ESM project a lot more feasible now than it was when ESM support in nodejs first came out as you can still access libraries in NPM that only support CommonJS.
Note also that more and more libraries in NPM are supporting direct loading from either module type so they are equally easy to use whether your project is CommonJS or ESM.
Over time, I would expect that any actively developed, shared module on NPM will eventually support direct loading as an ESM module. But, we're in this transition period of time where many have not yet implemented that or there are particular challenges in implementing the new loading scheme (which comes with it's own and different set of rules). In the meantime, you can still load CommonJS modules into ESM projects.
Existing CommonJS Projects
If I had an existing project that was CommonJS, I would not be spending any time thinking about converting it to ESM because there is still widespread support for CommonJS and my developer time is probably better spent on adding features, testing, fixing bugs, etc... than converting module formats.
Interoperability
One of the important interoperability things to know is that you can load a CommonJS module from an ESM module in several different ways:
With a static import: import { callMeCommon } from '../otherProject/common.js';
With a dynamic import import(someFile).then(...)
By using module.createRequire(import.meta.url) and then using that require() function to load your CommonJS module.
You can also use the dynamic import('someModule').then(...) to load an ESM module from a CommonJS module, but it's not synchronous like require() was so it has to be dealt with differently.
It's also useful to know that ESM modules do not have some nodejs niceties that CommonJS modules had. There's no automatic __dirname or __filename to tell you exactly where this file was loaded from. Instead, you have to parse those values out of import.meta.url and compute them. So, it's still possible to get that information, it's just not as convenient as it used to be.
On the other hand, ESM modules have some features like top level await that CommonJS do not which can be very useful.
Your Specific Questions
So, is CommonJS 'require' something that is still used in projects?
Yes, it is still used a lot.
Or is it slowly dying and needed only for maintaining legacy codes?
[Comments as of early 2022] I wouldn't so much say that it is dying as there are very few projects on NPM that don't still support CommonJS. In fact, when a project releases a new version that no longer supports CommonJS, it creates quite a problem for their user base (we see some of these issues here on stackoverflow) because of the significant prevalence of CommonJS projects still and people not familiar with how to load different module types.
So, I'd say that we're still in a very early stages of a transition from CommonJS to ESM and it's more like ESM is getting more and more adoption rather than CommonJS is dying. I'd hazard a guess that the majority of modules on NPM will, over the next few years, move to support direct loading from both module formats.
Transpiling
Lastly, developers often use transpiling to allow them to write code using the newest syntax and features, but using a transpiler to convert the code back to a lower, common denominator that runs anywhere. That can also be done for module architecture (write code in ESM, transpile to CommonJS).
A Few Interesting References
What does it take to support Node.js ESM?
Hybrid npm packages (ESM and CommonJS)
Node Modules at War: Why CommonJS and ES Modules Can’t Get Along
All you need to know to move from CommonJS to ECMAScript Modules (ESM) in Node.js
In Nodejs it doesn't yet appear to be deprecated given the LTS v20 precursor (v19) documentation describes the feature:
https://nodejs.org/api/esm.html#interoperability-with-commonjs
https://github.com/nodejs/Release
If locked into Nodejs the above affords a length of time into 2025 for using LTS Nodejs runtimes, with legacy code, and possibly polyfill the same whenever the Nodejs product changes status (the module system or the whole runtime).
CommonJS is the legacy module system specific to Nodejs
see https://wiki.commonjs.org/wiki/Modules/Meta
Generally outside of Nodejs Commonjs/require has been displaced in JS/TS runtimes by ESModules:
https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules
As much as possible I've been moving away from Nodejs to Deno and haven't looked at the specifics and details of its npm and Nodejs compatibility. For those who're looking to implement software using contemporary web platform features it's the best forward-looking with current working solutions that I'm aware of (more at https://deno.land/).
import { createRequire } from "https://deno.land/std/node/module.ts";
// import.meta.url...like `__filename` ...
const require = createRequire(import.meta.url);
const path = require("path");

What is difference between Module Loader and Module Bundler in JavaScript?

Can someone provide some information about Module Loaders and Module Bundlers in JavaScript?
What are the differences?
When should I use a Module Loader and when a Module Bundler?
Why do we need them at all?
Module loaders and bundlers both make it more actionable to write modular JavaScript applications. Let me give you some background:
Module loaders
A module loader is typically some library that can load, interpret and execute JavaScript modules you defined using a certain module format/syntax, such as AMD or CommonJS.
When you write modular JavaScript applications, you usually end up having one file per module. So when writing an application that consist of hundreds of modules it could get quite painful to make sure all files are included and in the correct order. So basically a loader will take care of the dependency management for you, by making sure all modules are loaded when the application is executed. Checkout some popular module loaders such as RequireJS and SystemJS to get an idea.
Module bundlers
Module bundlers are an alternative to module loaders. Basically they do the same thing (manage and load interdependent modules), but do it as part of the application build rather than at runtime. So instead of loading dependencies as they appear when your code is executed, a bundler stitches together all modules into a single file (a bundle) before the execution. Take a look at Webpack and Browserify as two popular options.
When to use what?
Which one is better simply depends on your application's structure and size.
The primary advantage of a bundler is that it leaves you with far fewer files that the browser has to download. This can give your application a performance advantage, as it may decrease the amount of time it takes to load.
However, depending on the number of modules your application has, this doesn't always have to be the case. Especially for big apps a module loader can sometimes provide the better performance, as loading one huge monolithic file can also block starting your app at the beginning. So that is something you have to simply test and find out.
ES6/ES2015 Update
Note that ECMAScript 2015 (or ES6) comes with it's own, native implementation of modules. You can get a quick intro here and here.

Is there still a need for a module loader with applications that use JavaScript ES6 modules and classes?

If you code an application using ES6 modules and classes, is there any need to use a module loader framework, or is the best practice to just use a build tool to concatenate all the code into a file (or files) and include those using a normal script tag?
Yes. Somebody, somewhere along the line has to load the module.
I think you're conflating compiling modules ahead of time vs loading them individually. Webpack is a module loader that outputs a single file for the browsers to use later, while the System API and requirejs et al load a number of individual files.
There are performance factors on both sides, particularly longer build time (when precompiling) vs longer load time (with multiple files).
Webpack, Browserify, and most other module loaders (with the notable exception of the System API) allow you to define some loaders for certain file types and automagically compile your (S)CSS or templates on the way through, as well as running other tools to uglify or obfuscate your code. The ES6 System API does not provide these features, but is a more robust runtime loader than most.
This boils down to two trade-offs:
support for non-JS modules (styles, templates) vs build time
single request and longer build vs many requests and short/no build
Evaluate them for your users (high-bandwidth vs mobile), environment (if you have two dozen CI agents, who cares if the build takes an extra 3s?), and stack (if you have a lot of template files, compiling them AOT could be important).

What to use requirejs and when to use browserify?

I read quite a few articles about requirejs and browserify. In 2 lines if one asks me to define it I will say.
AMD -> Client Side -> Implementaion : RequireJS -> asynchronous -> uese define keyword
CommonJs -> Server Side -> Implementaion : Browserify -> synchronous -> uses export keyword
What I want to understand is:-
Even being Server side why will one use browserify in client side?
When will you prefer one over other?
Webpack and Browserify don't replace requirejs directly, they replace the r.js optimizer that comes with require. Both modern bundlers run your code (and optionally stylesheets, templates, and other resources) through various filters, creating a single file with your whole application wrapped up.
RequireJS' asynchronous modules (AMD) has been replaced by the more commonly-used CommonJS and ES6 module definitions. This is, in part, so node modules play nicely with webpack and browserify, but also because the benefits of AMD largely stop applying when you have a single bundled file. Since all modules are available at the same time, you don't need the overhead of managing asynchronous loading.
Webpack and Browserify also expand on rjs' feature set, by adding extensive support for compiling templates, images, fonts, and other resources into your bundle. They can run transpilers (like Babel or Typescript), uglifiers, minifiers, and all sorts of other tools along the way. Webpack also supports outputting multiple bundles and loading them on-demand, while hiding most of those deployment details from the developer.

What is the best approach for TypeScript with ES6 modules?

I am starting a new Web project and trying TypeScript, mainly as an ES6 transpiler but also with the additional benefits of type checking, especially for existing libraries such as jQuery combined with the DefinitelyTyped type definitions.
Since the latest version, TypeScript supports both its own internal modules and ES6 modules, which calls "external" modules. Because ES6 is more standard than TypeScript, my intention is to use ES6/external modules rather than the traditional/internal TypeScript modules.
I have my own code defined in several files/modules, but I want the build to generate a single .js file that I can load from the browser.
The problem is that as far as I can tell, TypeScript is only able to generate a single output file when using its own module format. If I try to use ES6 external modules, then it generates a separate .js file for each .ts file.
This means I would need to concatenate them using browserify, but also I want source map support, which means that I should configure browserify for input and output source maps, then combine it with exorcist so the source map is extracted out of the bundle.
That looks like a very complex build setup. Isn't there a more straightforward way, maybe directly supported by TypeScript? What is the best approach? What do you recommend?
Let TypeScript do what it does best...
Add types to JavaScript be it ES5/ES6/ES7
Transpile to ES5
Resolve modules via the specified module syntax (commonjs, amd, umd, system)
Then find another tool that will take the separate files and combine them into a single bundled file (in the right order). My suggestions are to look into:
webpack
browserify
tsify
Are you looking for a solution in the browser? If so, I highly recommend my project Zwitterion. It removes the complicated build steps, and let's you include TypeScript directly into the browser with normal script tags. You can also use standard ES modules directly, with no extra setup. It uses SystemJS under the hood to achieve that. There is no source map support yet, but that should come. If you would like more information besides what's in the README, you can read "Zwitterion, forget the build step".

Categories