name
main
type
exports
imports
"type": "module" // or "commonjs", default "commonjs"
"main" - traditional entry point
"exports" - overrides "main" (can use both, but then "main" is only for older node)
prevents consumer from using anything not defined in package, including "package.json"
main entry: "exports": "./main.js", or "exports": { ".": "./main.js",
can map entry to source - e.g. "./lib": "./lib/index.js",
can export entire folder, -e.g. "./lib/*": "./lib/*.js",
expose everything "./*": "./*"
Conditional exports - provides different ES module (conditions: "import", "require", "node", "default"):
// package.json
{
"main": "./main-require.cjs",
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
},
"type": "module"
}
Subpath imports ("imports" keyword) can define a bookmark (must starts with "#") path and map to internal / external module.
Can also use conditions ("node", "default", etc.)
Maybe can work together with Typescript "baseUrl"?
Used by Node.js
modules
use function to wrap things in a closure
use "module.exports" to expose
only support objects as modules
to import, "require"
server-first, synchronous load
not for browser - loading stops everything else
unless webpack
Module example
function MyClass() {
this.hello = function() {
return 'hello!';
}
this.goodbye = function() {
return 'goodbye!';
}
}
module.exports = MyClass;
// export an object, which member can be imported using destructuring expression
module.exports = {
member1: value 1
}
Use example
// find from local/global path but not current path
var MyClass = require('MyClass');
// find from current path
var MyClass = require('./MyClass');
// destructuring assignment
// this only works, when the exported is an object, and start with var
// the reason is because if without var, { starts a block
var {member1} = require('./MyObjectModule')
var myModuleInstance = new MyClass();
myModuleInstance.hello(); // 'hello!'
myModuleInstance.goodbye(); // 'goodbye!'
Installation
npm list -g : list where global libraries are installed (or local without -g)
global libraries: /usr/local/lib/node or /usr/local/lib/node_modules, or NODE_PATH environment variable
local libraries: ./node_modules
As module_name.js or as module_name/index.js
require('./module_relative_to_path')
export {something} -> import {something} from "./somewhere.mjs"
"import" can load CommonJS modules - https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs
https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
"type":"module"
"exports":"entry.js"
"engines": {"node": "^12.20.0 || ^14.13.1 || >=16.0.0"}
// tsconfig.json
{
"module":"ES2020", //or higher
}
"moduleResolution": "Node" // this is about tsc module resolution, always use node
"module":
"commonjs" - emits "require()" etc, cannot import ESM module with dynamic import due to https://github.com/microsoft/TypeScript/issues/43329
"es2015" - emits ESM "import", however without support for dynamic import "import()", so best to just use ES2020
"es2020" - same as es2015 with support for dynamic import
"target":
"target" affects transpiring language features (await, yield, etc.) but not affect import codes
See https://www.typescriptlang.org/tsconfig#esModuleInterop, the problem is about working with package such as "moment", some inconsistency between CommonJS and ESM module system
require("moment") results in a callable, also a name space with other properties
require("moment").default -> undefined
When working with CommonJS package, either
When tsconfig is configured with "module":"commonjs"
when working with CommonJS module using "export =" (no default export), but default export a function (such as "moment")
/**
* Codes below only works with "esModuleInterop": false,
* with "esModuleInterop": true, type checking fails, "moment" cannot be called
*
* Error:
* Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.
*/
import * as moment from "moment"
console.log(moment())
/**
* Codes below only works with "esModuleInterop": true,
* with "esModuleInterop": false, type checking fails
*
* Error:
* Module '"/home/bing/tech-playground/tt/node_modules/moment/ts3.1-typings/moment"' can only be default-imported using the 'esModuleInterop' flag
* This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.
*/
import moment from "moment"
console.log(moment())