In the recent release Node v18.6.0
an experimental feature ESM Loader Hooks API was brought.
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.
Welcome to join the human high-quality front-end framework group , with flying
Reference for this article:
Custom ESM loaders: Who, what, when, where, why, how
Contents
Feature Introduction
Friends who have used webpack
must know that there is a concept of —3854b101fb8fb85f20b99bf6d03b594dc webpack
in loader
, which is used to load and process different types of files, such as css-loader
, url-loader
.
The execution order of —998ef07f30e4b586198fd5256c842b1f loader
depends on the order in which webpack
parses and traverses the file tree internally.
The ESM Loader Hooks
to be introduced today is similar to webpack loader
, except that the parsing and traversal of the file tree is supported by Node.js
natively supported ESM
(rather than packaging tools) determined.
By defining different loader
, you can process each ESM
module in the project without using engineering tools .
For example, after enabling the feature through the --experimental-loader
command on the command line, execute the following statement:
$> node --loader redirect.mjs app.mjs
其中, app.mjs
的源文件, .mjs
文件是个ESM
模块(相对应的, .cjs
Refers to the CJS
module).
--loader
自定义的ESM Loader
,这里redirect.mjs
, app.mjs
会redirect.mjs
deal with.
redirect.mjs
code is as follows:
// 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.mjs
will rewrite the import path of the file according to the current environment of Node .
For example, in the development environment ( process.env.NODE_ENV === 'development'
), app.mjs
redirect.mjs
will be redirected to app.dev.mjs
ESM Loader Hooks API
contains the word Hooks
because each custom ESM Loader can be connected to other custom ESM Loaders like hooks (or Node.js
–provided default Node.js
ESM Loader
).
For example in the following statement:
$> node --loader c.mjs --loader b.mjs --loader a.mjs app.mjs
app.mjs
will go through a b c
three custom ESM Loaders in sequence.
The whole process is like a chain of promise.then
(in fact, each ESM loader
does return a promise
).
Practical examples
For an example closer to day-to-day development, consider the following ESM
module:
// 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 parts Node.js
that cannot be processed, such as:
TS
syntax (need to be compiled intoJS
and process the file descriptor asNode.js
recognizable form)JSX
conversion (need to compile toReact.createElement
orjsxRuntime.jsx
)- Need to process the imported
CSS
file - Need to handle remotely imported modules (statements introduced in the code
routes
)
Working with CSS files
Taking the processing of the CSS
file as an example, suppose the content of the CSS
file is as follows:
.Container { border: 1px solid black; } .SomeInnerPiece { background-color: blue; }
For testing purposes, you only need to generate a snapshot corresponding to the class name, so you can implement a simple CSS loader
, process the input CSS
file, and output the result as Node.js
JSON
Format:
{ "Container": "Container", "SomeInnerPiece": "SomeInnerPiece" }
Reference: Simple implementation of CSS loader
Handling remotely imported modules
Another example is a module that handles remote imports .
当识别到https://
( import
声明或import()
)时, 可以利用https
module initiates a request and returns the request corresponding to promise
:
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', shortCircuit: true, source: data, })); }).on('error', err => reject(err)); }); } return nextLoad(url, context); }
Reference: Simple implementation of Https loader
Summarize
When the ESM Loader Hooks
features tend to be stable, and the supporting loader
ecology is rich enough, many engineering requirements that originally required packaging tools can be used Node.js
native solution .
For example, to process the app.tsx
file mentioned above, 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?