preface

Front-end development to now, community ecology has been very rich.

Thanks to the efforts of numerous open source champions, many of the front-end development pain points (such as static type checking, browser compatibility) already have de facto standard solutions (e.g. TS, Babel).

Underneath this boom, however, there is a problem that is not readily apparent to daily development:

The confusion of modular specifications

Have you ever encountered an inexplicable bug, searched for resources, verified repeatedly, and finally found it after hours:

It turns out that a package exports CJS and the project uses ESM.

Take this example: remember a package compression error

If you think this is an easy problem to spot, how about combining layers of dependencies on node_modules?

This problem reveals the tip of the iceberg of the struggle and game between modular specifications.

As the cornerstone of modern front-end engineering, modular specifications have a lot to learn.

I will spend several articles on modularity specifications. This is the first article to focus on the evolution of modular specifications.

The body of the

What was the biggest headache on the front end ten years ago? It must be browser compatibility.

With the advent of compilation tools such as Babel, compatibility is increasingly resolved by engineering solutions (ES6+ to ES5).

Not only compatibility issues, but everyday development requirements such as DSL (e.g. JSX, VUE’s template syntax), code compression, code static checking (TS) can all be solved in an engineering solution.

If today’s flourishing front-end engineering ecosystem is likened to a mansion, its foundation must be modular specifications.

Modern JS code is organized around modular specifications, so let’s look at the building from the bottom up:

The implementation of the specification depends on the host environment, for example, the browser environment implements the EcmaScript Module (ESM) specification.

Node V12 supports the CommonJS specification, and Node V12 supports both CJS and ESM.

On top of the host environment are toolsets based on modular specification implementations such as webpack, vite, VScode ecology.

Further up, you can implement a variety of engineering tools based on the apis provided by the toolset. Such as:

  • webpack loader

  • VScode plugin

  • babel plugin

On top of that, there is business code written by the developers themselves.

Developers only need to configure the tools in the toolset to serve the business code. Such as:

  • Configuring eslint (tools) in VScode (toolset) will prompt you at development time

  • Configure the Babel Loader in WebPack to use ES6+ syntax at development time

As you can see, the underlying modular specification implementation is ideally not a concern from the developer’s perspective.

The battle for specification

However, things are dynamic and modular specifications don’t happen overnight, so let’s go back to 2009.

Ryan Dahl, an American programmer, created the Node.js project to use JS for server-side development.

Node.js uses the CJS standard as a modularity specification.

With the Server Module Specification (CJS), it is natural for JS developers to want to provide a modular specification for clients (mainly browsers).

However, CJS is designed for the server side.

On the server side, IO operations usually complete quickly, so the CJS specification defines:

Module load –> module parse –> Module execution

The process is executed synchronously as a whole.

In the browser environment, however, module loading (that is, data requests) is often time consuming. Someone once made a vivid analogy:

If a CPU cycle takes 1 second to complete, the network request for the file takes 4 years to complete.

Clearly a browser-side modular specification that supports asynchrony is needed.

The AMD (Asynchronous Module Definition) specification is a product of such requirements.

However, the specifications proposed by these communities were ultimately intended to address a temporary need, and new modular specifications poured in and died out over the course of history.

Until the ESM specification was proposed.

The ESM specification is a modular specification of the ES standard, and its early discussions date back to 2019.

You can see the ES-Module-history of the ESM specification here

The ESM divides the module specification into three phases:

Module load –> module instantiation –> Module execution

Module loading is done by a Loader provided by the host environment (for example, in a browser environment, the behavior of the Loader is defined by the HTML specification).

Module instantiation and module execution the ESM specification defines the execution flow.

Unlike the synchronous execution of the CJS specification, the ESM specification disassembs the process into three separate phases.

Whether a module loads synchronously or asynchronously depends on the host environment.

Support for different host environments, smooth out multiple differences, more power than other specifications (more on that later), and pure pedigree (ES official),

Making a unified front end of ESM specification seem imminent.

However, at this point the community already had a large number of open source packages and components based on the CJS specification, and they could not immediately switch to the ESM specification.

So, the JS ecosystem is in a state where cJS-compliant libraries and ESM-compliant libraries coexist for a long time.

But in the end, the ESM specification is bound to dominate, because it has so many advantages (again, more on that later).

Opportunities for fragmentation of norms

The current confusion of modular specifications is an opportunity for open source gurus.

To allow developers to focus more on the business rather than the adaptation of module specifications.

Many open source toolsets attempt to bridge the modularity gap, such as:

  • Using babel-plugin-transform-commonjs in Babel converts the code from the CJS specification to the ESM specification

  • In order to solve the incompatibility of ESM, CJS and script label import, a Universal Module Definition (UMD) specification compatible with the three formats is proposed

Some toolsets take advantage of modular specification differences to differentiate themselves from competitors. For example:

  • browserifyThe selling point of this packaging tool is: UseCJSSpecification packaging, so that a piece of code at the same timeNodeEnvironment and browser environment (packaged) execution.

In the browser environment, Node’s core libraries (events, Stream, path…) Will be packaged into a browser-supported version.

  • ViteinDEVEnvironment useESMSpecification of dependencies between building blocks.

It relies on most modern browsers to support the ESM specification natively, eliminating the packaging process and making it much faster to compile.

  • rollupA native ofESMProvide more support.

Strict support for ESM specifications and better static analysis enabled Rollup to deliver superior treeShaking capabilities.

Become the first choice for more library packaging tools.

Compete with big, holistic solutions like Webpack.

The pain of fragmentation of norms

As you can see, due to the fragmentation of modular specification support in the underlying host environment, the upper toolset is required to smooth out the differences in module specifications.

Imagine a project that uses WebPack, Babel, and TS together.

All three toolsets are compatible with multiple module specifications. Such as:

When using Babel alone, for the following code:

import a from 'lib';
console.log(a);
Copy the code

Will be compiled by Babel to:

"use strict";

var _lib = _interopRequireDefault(require("lib"));

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

console.log(_lib.default);
Copy the code

The default export of the ESM is compiled into an object containing the default attribute.

You can try Babel Playground

When multiple toolsets are working on the same project doing the same thing for their respective purposes (smoothing out modular specification differences),

Once one of the plug-in configurations in the toolchain does not meet expectations, or a package that does not meet expectations is introduced, the hard debug begins……

dawn

Despite the current inconveniences, history is unstoppable, and modular specifications that have been thrown under the wheel of history and crushed will fade into oblivion.

And the winner is bound to take all.

Why is the ESM set to be the biggest winner? What advantage does he have that he can’t match? We’ll find out in the next article.