From the public number: Magician Carson

Hello everyone, I'm Casson.

In a recent release Node v18.6.0, an experimental feature ESM Loader Hooks API [1] was introduced .

If he finally landed, it is likely to become a feature that changes the future of front-end engineering. Let's talk about him in this article.

Reference for this article:

Custom ESM loaders: Who, what, when, where, why, how [2]

Feature Introduction

Friends who have used webpackit must know webpackthat there is a loaderconcept in it, which is used to load and process different types of files, such css-loaderas url-loader.

loaderThe order of execution depends on the order webpackin which the file tree is parsed and traversed internally.

What we will introduce today is ESM Loader Hookssimilar webpack loader, except that the parsing and traversal of the file tree Node.jsis ESMdetermined by the natively supported specification (rather than the packaging tool).

With different definitions loader, each module in the project can be processed without the use of engineering tools .ESM

For example, after --experimental-loaderenabling the feature through the command on the command line, execute the following statement:

$> node --loader redirect.mjs app.mjs

Among them, app.mjsit is "source file to be processed" , and .mjsthe suffix indicates that the file is a ESMmodule (correspondingly, the .cjssuffix refers to a CJSmodule).

--loaderUsed to specify custom , what ESM Loaderis specified here will be handed over to processing.redirect.mjsapp.mjsredirect.mjs

redirect.mjscode show as below:

// redirect.mjs
export function resolve(specifier, context, nextResolve{
  let redirect = 'app.prod.mjs';

  switch(process.env.NODE_ENV) {
    case 'development':
      redirect = 'app.dev.mjs';
      break;
    case 'test':
      redirect = 'app.test.mjs';
      break;
  }

  return nextResolve(redirect);
}

redirect.mjsThe import path of the file will be rewritten according to "Node's current environment" .

For example, in the development environment ( process.env.NODE_ENV === 'development'), after app.mjsprocessing redirect.mjs, it will be redirected to app.dev.mjs.

ESM Loader Hooks APIThe reason for the Hooksword in it is because each "custom ESM Loader" can connect to other "custom ESM Loaders" (or Node.jsdefault provided ) like hooks ESM Loader.

For example in the following statement:

$> node --loader c.mjs --loader b.mjs --loader a.mjs app.mjs

app.mjsIt will go through a b cthree "custom ESM Loaders" in turn .

The whole process is like a promise.thenchain (in fact, each ESM loaderdoes return one promise).

Practical examples

For an example closer to day-to-day development, consider the following ESMmodules:

// app.tsx
import ReactDOM from 'react-dom/client';
import {
  BrowserRouter,
  useRoutes,
from 'react-router-dom';

import App from './AppHeader.tsx';

import routes from 'https://example.com/routes.json' assert { type'json' };

import './global.css' assert { type'css' };

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <BrowserRouter>
    <App />
    <main>{useRoutes(routes)}</main>
  </BrowserRouter>

);

This includes many Node.jsparts that cannot be processed, such as:

  • TSSyntax (needs to be compiled into JS, and to process file descriptors into Node.jsa recognizable form)

  • JSXConvert (need to compile to React.createElementor jsxRuntime.jsx)

  • Incoming files need to be processedCSS

  • Need to handle remotely imported modules (introduced routesstatements in the code)

Working with CSS files

Take processing CSSfiles as an example, assuming the CSScontents of the files are as follows:

.Container {
  border1px solid black;
}

.SomeInnerPiece {
  background-color: blue;
}

If you only need to generate a snapshot corresponding to the class name for testing purposes, you can implement a simple CSS loaderprocess that processes the input CSSfile and outputs the result in an Node.jsexecutable JSONformat:


  "Container""Container"
  "SomeInnerPiece""SomeInnerPiece"
}

Reference: Simple implementation of CSS loader [3]

Handling remotely imported modules

Another example is the handling of "processing remotely imported modules" .

When the https://beginning of the file descriptor (that is , the part of the string in the importdeclaration or expression) is identified, the module can be used to initiate a request and return the request corresponding to :import()httpspromise

import { get } from 'https';

export function load(url, context, nextLoad) {
  if (url.startsWith('https://')) {
    return new Promise((resolve, reject) => {
      get(url, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => resolve({
          format'module',
          shortCircuittrue,
          source: data,
        }));
      }).on('error', err => reject(err));
    });
  }

  return nextLoad(url, context);
}

Reference: Simple implementation of Https loader [4]

Summarize

When the ESM Loader Hooksfeatures become stable and the supporting loaderecosystem is rich enough, many engineering requirements that originally required packaging tools can be Node.jssolved natively.

For example, to process the file mentioned above app.tsx, just execute the following command:

$> node --loader typescript-loader --loader css-loader --loader network-loader app.tsx

How much impact do you think this feature will have on future front-end engineering?

References

[1]

ESM Loader Hooks API:https://nodejs.org/en/blog/release/v18.6.0/

[2]

Custom ESM loaders: Who, what, when, where, why, how:https://dev.to/jakobjingleheimer/custom-esm-loaders-who-what-when-where-why-how-4i1o

[3]

Simple implementation of CSS loader:https://github.com/JakobJingleheimer/demo-css-loader/blob/main/loader.mjs

[4]

Simple implementation of Https loader:https://github.com/nodejs/loaders-test/blob/835506a638c6002c1b2d42ab7137db3e7eda53fa/https-loader/loader.js

--- EOF ---


Recommended↓↓↓