directory
- preface
- Node support for CJS and ESM
-
- We can solve it with suffixes
-
- Resolve the problem by using the Type field
-
- – input – type logo
- doubt
-
- The module field is off
- Disadvantages of the main field
- Exports of Kings
-
- Scope of the package
-
- Pattern of subpaths
-
- Conditional export
-
- In duplicate
- conclusion
- reference
preface
Node has a very core knowledge point — module. In the era when front-end modularization has not really come, the solution given by Node is CommonJS for CJS.
Later, ECMAScript adopted the JS modular system, thus opening up the situation of CJS and ESM co-existence. With the popularity of modularization today, have you ever wondered why most packages can be used through BOTH CJS and ESM?
So let’s look at this principle.
The ESM and CJS:
- Esm-ecmascript module
- CJS – CommonJS
Node support for CJS and ESM
Node supports CJS by default, which we all know, and then supports ESM, so what changes have been made to Node?
1. We can solve it with suffixes
- The ESM to
.mjs
At the end. - CJS in
.cjs
At the end.
2. Use the Type field to resolve the problem
- CJS in
.js
End of file, and most recent parentpackage.json
Middle-top-level field"type"
A value of"commonjs"
. - The ESM to
.js
End of file, and most recent parentpackage.json
The top of the"type"
A value of"module"
.
3. --input-type
mark
-
Pass the flag –input-type=commonjs as an argument to –eval or –print, or to Node via STDIN.
-
Pass a string marked –input-type=module as an argument to –eval or through STDIN to Node
doubt
However, normally a package can only support one type of module, which is either ESM or CJS.
But you notice that most packages can be used with require and import. How is that?
The module field is off
We support ESM by using Node’s native support for CJS to support require syntax, and by using Webpack and other packaging tools to identify the module field of package.json. It’s also tree-shaking relative to require.
Disadvantages of the main field
- The primary disadvantage of the main field is that it does not support both formats.
- Files inside package cannot be isolated and can be referenced at will, for example, I referenced Chalk
package.json
The file can import the relative pathnode_modules/chalk/package.json
.
The new Module from Exports and the packaged tool-supported module is similar, but Exports has Node’s native support and is even more powerful.
Exports of Kings
Exports have three important functions:
- Scope package.
- Subpath mode
- Conditional export
Exports has other features that are not the focus of today’s article, so I’ll skip them.
1. Scope packages
Exports and main fields are mutually exclusive. If you define both “exports” and “main”, “exports” will override “main” in Node that supports “exports” (version v12.7.0 or higher). Otherwise, “main” takes effect.
So we can use exports by simply copying the main field and changing it to exports, like this:
{
"main": "./index.js"."exports": "./index.js"
}
Copy the code
It is important to note that if the exports field is valid, you cannot refer to unexported files in the package, unlike the main field, which is the scoped package.
We’ll take a look at the Calculator demo we created in the previous article using the workspaces field described in the new way to replace NPM Link debugging.
Export default (STR) => STR; export default (STR) => STR; , the current folder directory
. ├ ─ ─ packages │ ├ ─ ─ divide │ │ ├ ─ ─ index. The js │ │ └ ─ ─ package. The json │ ├ ─ ─ minus │ │ ├ ─ ─ subpath. Js │ │ ├ ─ ─ index. The ts │ │ └ ─ ─ package. Json │ ├ ─ ─ plus │ │ ├ ─ ─ index. The js │ │ └ ─ ─ package. The json │ └ ─ ─ times │ ├ ─ ─ index. The js │ └ ─ ─ package. The jsonCopy the code
Minus’s package.json folder now looks like this:
{
"main": "index.js"
}
Copy the code
We can refer to the files in the package whenever we want, now we can introduce subpath.js in the root directory index.js:
import subpath from "minus/subpath.js";
console.log(subpath("Hi JavaScript"));
Copy the code
However, we did not work with exports:
{
"main": "index.js",
"exports": "./index.js"
}
Copy the code
When the “exports” field is defined and all child paths are closed, debug raises the error ERR_PACKAGE_PATH_NOT_EXPORTED.
By the way, the default installation package of NPM should really learn from PNPM and do the package sealing function.
2. Subpath pattern
Okay, subpath closure is great, but sometimes we just want one feature of a package, like Lodash giving us the ability to import on demand.
This requires a subpath pattern, which is essentially a path map.
As an example, let’s do path mapping under exports mode.
"exports": {
".": "./index.js",
"./subpath.js": "./subpath.js"
},
Copy the code
The ERR_PACKAGE_PATH_NOT_EXPORTED error is gone when debugging the code.
3. Export conditions
Conditional export is very simple.. Represents the current directory.
"exports": {
".": {
"import": "./index.mjs"."require": "./index.cjs"}},Copy the code
When you use this package, Node will resolve the corresponding module specification depending on the user or downstream package environment. Now I can import it in projects that support import environments, or require it in projects that support require.
In duplicate
Since the package needs to support two modularity, the problem is that it is impossible for us to write the code in two parts. We have to use packaging tools, Webapck and Rollup, but their configuration is too complicated. After you finish the environment, the inspiration and mood for writing the code will probably be lost. Today we introduce a small and beautiful tool called TSUP.
Tsup is also perfect for using Typescript with zero configuration:
$ tsup src/index.ts
Copy the code
You can then publish dist/index.js in your project root directory.
Of course, our focus is on dual-format modules, so support dual-format with just a flag:
$ tsup src/index.ts --format cjs,esm
Copy the code
Both dist/index.js and dist/index.mjs are generated together, which is very Nice.
Here’s a copy of the preferred template for package.json:
{
"name": "calculator"."main": "./dist/index.js"."module": "./dist/index.mjs"."types": "./dist/index.d.ts"."exports": {
".": {
"require": "./dist/index.js"."import": "./dist/index.mjs"."types": "./dist/index.d.ts"}},"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts --clean"."watch": "npm run build -- --watch src"."prepublishOnly": "npm run build"}}Copy the code
Having said that, I also highly recommend trying out the surprisingly fast Esbuild.
conclusion
Today, looking back at modularity and recognizing the coexistence of CJS and ESM, Node has moved with The Times to support dual packages, and to compensate for the potential abuse of unexported packages, Node has improved its functionality along the way.
I don’t see any open source projects using this feature yet, but I’m sure you’ll see it in major open source projects in the future.
reference
- publish-esm-and-cjs
- Node latest Module import/export specification