Preface
Except for the ancient C/C++, almost all programming languages have module systems and official package managers. We generally do not implement all the code ourselves, and use open source libraries and frameworks extensively during actual application development. This article demonstrates how to turn the library you implement into a package. A package is your application and your library.
As the program grows bigger, we will put code for different purposes into different source files. For code sharing, we will propose some of the code as a library. If our project becomes more and more complex, we will have both libraries and executable programs. How to organize the code of a project and how to understand the code structure of a complex project. Just two points to master:
- Mechanisms for understanding the modules or packages of the language itself
- Understand how a package manager or build system builds libraries/programs
The JavaScript language itself does not have packages
JavaScript packages are not a language-level concept, but a package manager-level concept. In other words, the JavaScript language itself does not have packages. Packages are a feature of npm, allowing you to build, test, and share JS modules.
JavaScript has only modules, one.js
A file is a JavaScript module.
The JavaScript module is equivalent to a package in Go language, but the package in Go language can be multiple in a single directory..go
document.
The Go language code is organized through packages, similar to libraries or modules in other languages. A package consists of one or more located in a single directory
.go
The source code file is composed, and the directory defines the function of the package. Each source file has onepackage
The statement starts, in this example,package main
, indicates which package the file belongs to, followed by a series of imported packages, followed by program statements stored in the file.
npm package
BTW: Rust is also like JavaScript. The Rust language itself does not have the concept of packages, and the Rust language itself only has Crate and Module. Rust's Module is a namespace and separates the code into different source files. Cargo's package can only have one Library Crate and multiple Binary Crates. rustc Consider one crate at a time.
node considers one JS module at a time.node
Run a JS module. The npm package can only have one library and can have multiple executable scripts. The name of the library isIn-house
name
This is also the name of the bag."main"
Fields are the entrance to this library. A library is also a package.A package is described.
Let's create a package and use it.
npm init
Create a JavaScript package, that is, create. Create greeting
mkdir greeting
cd greeting
npm init -y
We want to modify,
type: module
Tell NodeJS that the JS files in this package are all ES modules. The user of greeting must make this modification if he wants to use import to import a package or module.
--- i/
+++ w/
@@ -2,6 +2,7 @@
"name": "greeting",
"version": "1.0.0",
"main": "",
+ "type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
main:
It is the entrance to this package, we want to create this. It is the default main, we can customize the main.
// Filename:
export function hello(name) {
return `Hi, ${name}. Welcome!`
}
In this way, we have built a JavaScript package, which provides a hello function.
Create a package called hello and use greeting.
mkdir hello
cd hello
npm init -y
After creating the hello package, the directory structure is as follows
<home>/
├── greeting
│ ├──
│ └──
└── hello
└──
Using greeting, we need to install this package and execute it in the hello folder:
npm i ../greeting
Install the greeting package, npm creates a node_modules directory and puts the greeting code into node_modules. Because this is a local package, npm creates a symbolic link to the greeting directory.
node_modules/
└── greeting -> ../../greeting
We have not written any code in the hello package. We use the hello function provided by greeting in the hello package.
// Filename: hello/
import { hello } from "greeting"
const message = hello("Mikami Yua")
(message)
Then we run the following under hello.
$ node
(node:2544) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/user/hello/ is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /home/user/hello/.
(Use `node --trace-warnings ...` to show where the warning was created)
Hi, Mikami Yua. Welcome!
It is the entrance to our hello program. NodeJS will automatically find other js modules referenced by the JS module.
Here is a warning, and we want to eliminate this warning, add it in hello/"type": "module"
. NodeJS uses CommonJS by default, and ES module will be tried after CommonJS fails.
- See also: Modules: Packages | v23.11.0 Documentation
npm is not only a package manager, but also a front-end for package building tools.npm build
Build this project,npm install
Install project dependencies. Behind it may be the call to esbuild or other tools.
A package is equivalent to a library. You can introduce a certain module of the library, and all JS files are a module. You definitely don't want all JS files to be public. Some of the code is used internally in the library, not API, and may change the directory structure of the file in the future and even delete some internal functions.There is another
"exports"
Field, explicitly declare which modules of this package are public.
We modify the greeting package and use"exports"
, recommended for modern JS projects
--- i/
+++ w/
@@ -1,11 +1,11 @@
{
"name": "greeting",
"version": "1.0.0",
- "main": "",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
+ "exports": "./",
"keywords": [],
"author": "",
"license": "ISC",
Move the hello function in .
// Filename: greeting/
// re-rexport hello
export { hello } from "./"
// Filename: greeting/
export function hello(name) {
return `Hi, ${name}. Welcome!`
}
We have changed the structure of the greeting package, but still provide the hello function. After the greeting changes, the code of the hello package does not need to be changed, and it can still be usednode
run.
As the author of the greeting library, we know that the hello function is provided by greeting/, and I want to import the hello function directly from the corresponding JS module.
--- i/
+++ w/
@@ -1,5 +1,5 @@
// Filename: hello/
-import { hello } from "greeting"
+import { hello } from "greeting/"
const message = hello("Mikami Yua")
(message)
We ran the code and got an ERR_PACKAGE_PATH_NOT_EXPORTED error."exports"
It defines which modules are disclosed.
$ node
node:internal/modules/esm/resolve:314
return new ERR_PACKAGE_PATH_NOT_EXPORTED(
^
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './' is not defined by "exports" in /home/user/hello/node_modules/greeting/ imported from /home/user/hello/
at exportsNotFound (node:internal/modules/esm/resolve:314:10)
at packageExportsResolve (node:internal/modules/esm/resolve:662:9)
at packageResolve (node:internal/modules/esm/resolve:842:14)
at moduleResolve (node:internal/modules/esm/resolve:926:18)
at defaultResolve (node:internal/modules/esm/resolve:1056:11)
at (node:internal/modules/esm/loader:654:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:603:25)
at (node:internal/modules/esm/loader:586:38)
at (node:internal/modules/esm/loader:242:38)
at ModuleJob._link (node:internal/modules/esm/module_job:135:49) {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}
v22.13.0
If we remove exports from, we can import from any module according to the directory structure of the package.
ES Module of NodeJS
- Modules: Packages | v23.11.0 Documentation
The documentation describes what you would treat as an ES Module.of
type
yes"module"
, treat the input as an ES Module..js
The syntax of ES6 Module is used in the file, which is an ES Module.
Summarize
Take away message: JavaScript itself has only modules, and the concept of packages is established by npm and .Defines a JavaScript package. The exports field specifies which modules of this package are public, and the disclosed modules can be imported by the user.
JavaScript's package management method is very similar to Rust's package management method, and a package tends to be just one library, or to provide multiple executable files.
Reading materials:
- About packages and modules | npm Docs
- Modules: Packages | v23.11.0 Documentation