Spec-Zone.ru › Node.js 24 LTS

Модули: модули CommonJS

Стабильность: 2 - Стабильно

Модули CommonJS — это исходный способ упаковки кода JavaScript для Node.js. Node.js также поддерживает стандарт модулей ECMAScript, используемый браузерами и другими средами выполнения JavaScript.

В Node.js каждый файл рассматривается как отдельный модуль. Например, рассмотрим файл с именем foo.js:

const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`); copy

В первой строке foo.js загружает модуль circle.js, который находится в том же каталоге, что и foo.js.

Вот содержимое circle.js:

const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r; copy

Модуль circle.js экспортирует функции area() и circumference(). Функции и объекты добавляются в корень модуля путем указания дополнительных свойств специального объекта exports.

Переменные, локальные для модуля, будут приватными, поскольку модуль оборачивается в функцию средой Node.js (см. обертку модуля). В этом примере переменная PI является приватной для circle.js.

Свойству module.exports можно присвоить новое значение (например, функцию или объект).

В следующем коде bar.js использует модуль square, который экспортирует класс Square:

const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`); copy

Модуль square определен в square.js:

// Assigning to exports will not modify module, must use module.exports
module.exports = class Square {
  constructor(width) {
    this.width = width;
  }

  area() {
    return this.width ** 2;
  }
}; copy

Система модулей CommonJS реализована во встроенном модуле module.

Включение

Node.js имеет две системы модулей: модули CommonJS и модули ECMAScript.

По умолчанию Node.js будет считать модулями CommonJS следующее:

  • Файлы с расширением .cjs;

  • Файлы с расширением .js, когда ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "commonjs".

  • Файлы с расширением .js или без расширения, если ближайший родительский файл package.json не содержит поля верхнего уровня "type" или ни в одной из родительских папок нет файла package.json; за исключением случаев, когда файл содержит синтаксис, вызывающий ошибку, если он не вычисляется как модуль ES. Авторам пакетов следует включать поле "type" даже в пакеты, где весь исходный код написан на CommonJS. Явное указание поля type пакета упростит инструментам сборки и загрузчикам определение того, как следует интерпретировать файлы в пакете.

  • Файлы с расширением, отличным от .mjs, .cjs, .json, .node или .js (если ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "module", эти файлы будут распознаваться как модули CommonJS только в том случае, если они подключаются через require(), а не при использовании в качестве точки входа программы из командной строки).

См. раздел Определение системы модулей для получения более подробной информации.

Вызов require() всегда использует загрузчик модулей CommonJS. Вызов import() всегда использует загрузчик модулей ECMAScript.

Доступ к главному модулю

Когда файл запускается напрямую из Node.js, для require.main устанавливается его module. Это означает, что можно определить, был ли файл запущен напрямую, проверив require.main === module.

Для файла foo.js это значение будет true при запуске через node foo.js, но false при запуске через require('./foo').

Если точка входа не является модулем CommonJS, значением require.main будет undefined, и главный модуль окажется недоступен.

Советы для пакетных менеджеров

Семантика функции Node.js require() была разработана достаточно универсальной для поддержки разумных структур каталогов. Программы управления пакетами, такие как dpkg, rpm и npm, при необходимости смогут создавать нативные пакеты из модулей Node.js без изменений.

Ниже предлагается структура каталогов, которая может подойти:

Предположим, мы хотим, чтобы папка /usr/lib/node/<some-package>/<some-version> содержала содержимое определенной версии пакета.

Пакеты могут зависеть друг от друга. Для установки пакета foo может потребоваться установить определенную версию пакета bar. Пакет bar сам может иметь зависимости, и в некоторых случаях они могут даже конфликтовать или образовывать циклические зависимости.

Поскольку Node.js определяет realpath любых загружаемых модулей (то есть разрешает символические ссылки), а затем ищет их зависимости в папках node_modules, данную ситуацию можно разрешить с помощью следующей архитектуры:

  • /usr/lib/node/foo/1.2.3/: содержимое пакета foo, версия 1.2.3.
  • /usr/lib/node/bar/4.3.2/: содержимое пакета bar, от которого зависит foo.
  • /usr/lib/node/foo/1.2.3/node_modules/bar: символическая ссылка на /usr/lib/node/bar/4.3.2/.
  • /usr/lib/node/bar/4.3.2/node_modules/*: символические ссылки на пакеты, от которых зависит bar.

Таким образом, даже при обнаружении цикла или конфликтов зависимостей каждый модуль сможет получить подходящую для использования версию своей зависимости.

Когда код в пакете foo выполняет require('bar'), он получает версию, на которую указывает символическая ссылка в /usr/lib/node/foo/1.2.3/node_modules/bar. Затем, когда код в пакете bar вызывает require('quux'), он получает версию, на которую указывает символическая ссылка в /usr/lib/node/bar/4.3.2/node_modules/quux.

Кроме того, чтобы сделать процесс поиска модулей еще более оптимальным, вместо размещения пакетов непосредственно в /usr/lib/node мы можем поместить их в /usr/lib/node_modules/<name>/<version>. Тогда Node.js не будет искать отсутствующие зависимости в /usr/node_modules или /node_modules.

Чтобы сделать модули доступными для REPL Node.js, может быть полезно также добавить папку /usr/lib/node_modules в переменную окружения $NODE_PATH. Поскольку поиск модулей с использованием папок node_modules является относительным и основывается на реальном пути файлов, выполняющих вызовы require(), сами пакеты могут находиться где угодно.

Загрузка модулей ECMAScript с помощью require()

История изменений
Версия Изменения
v23.0.0, v22.12.0

Поддержка экспорта интероперабельности 'module.exports' в require(esm).

v23.5.0, v22.13.0, v20.19.0

Эта функция больше не выводит экспериментальное предупреждение по умолчанию, хотя предупреждение все еще может быть выведено с помощью --trace-require-module.

v23.0.0, v22.12.0, v20.19.0

Эта функция больше не скрыта флагом командной строки --experimental-require-module.

v22.0.0, v20.17.0

Добавлено в: v22.0.0, v20.17.0

Стабильность: 1.2 - Кандидат в релизы

Расширение .mjs зарезервировано для модулей ECMAScript. См. раздел Определение системы модулей для получения дополнительной информации о том, какие файлы анализируются как модули ECMAScript.

require() поддерживает загрузку только тех модулей ECMAScript, которые соответствуют следующим требованиям:

  • Модуль является полностью синхронным (не содержит верхнеуровневого await); и
  • Выполняется одно из следующих условий:
    1. Файл имеет расширение .mjs.
    2. Файл имеет расширение .js, и ближайший package.json содержит "type": "module"
    3. Файл имеет расширение .js, ближайший package.json не содержит "type": "commonjs", и модуль содержит синтаксис ES-модулей.

Если загружаемый ES-модуль соответствует требованиям, require() может загрузить его и вернуть объект пространства имен модуля. В этом случае поведение аналогично динамическому import(), но выполняется синхронно и напрямую возвращает объект пространства имен.

При наличии следующих ES-модулей:

// distance.mjs
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); } copy
// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
} copy

Модуль CommonJS может загрузить их с помощью require():

const distance = require('./distance.mjs');
console.log(distance);
// [Module: null prototype] {
//   distance: [Function: distance]
// }

const point = require('./point.mjs');
console.log(point);
// [Module: null prototype] {
//   default: [class Point],
//   __esModule: true,
// } copy

Для обеспечения совместимости с существующими инструментами, преобразующими ES-модули в CommonJS, которые затем могут загружать реальные ES-модули через require(), возвращаемое пространство имен будет содержать свойство __esModule: true, если оно имеет экспорт default, чтобы сгенерированный инструментами вызывающий код мог распознавать экспорты по умолчанию в реальных ES-модулях. Если пространство имен уже определяет __esModule, оно не будет добавлено. Это свойство является экспериментальным и может измениться в будущем. Его следует использовать только инструментам, преобразующим модули ES в модули CommonJS, следуя существующим соглашениям экосистемы. Коду, написанному непосредственно на CommonJS, следует избегать зависимости от него.

Результатом, возвращаемым require(), является объект пространства имен модуля, который помещает экспорт по умолчанию в свойство .default, подобно результатам, возвращаемым import(). Чтобы настроить, что именно должно возвращаться напрямую вызовом require(esm), модуль ES может экспортировать желаемое значение, используя строковое имя "module.exports".

// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
}

// `distance` is lost to CommonJS consumers of this module, unless it's
// added to `Point` as a static property.
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }
export { Point as 'module.exports' } copy
const Point = require('./point.mjs');
console.log(Point); // [class Point]

// Named exports are lost when 'module.exports' is used
const { distance } = require('./point.mjs');
console.log(distance); // undefined copy

Обратите внимание, что в приведенном выше примере при использовании имени экспорта module.exports именованные экспорты будут недоступны для потребителей CommonJS. Чтобы потребители CommonJS могли продолжать обращаться к именованным экспортам, модуль может сделать экспорт по умолчанию объектом с прикрепленными к нему свойствами именованных экспортов. Например, в приведенном выше примере distance можно прикрепить к экспорту по умолчанию, классу Point, в качестве статического метода.

export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }

export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  static distance = distance;
}

export { Point as 'module.exports' } copy
const Point = require('./point.mjs');
console.log(Point); // [class Point]

const { distance } = require('./point.mjs');
console.log(distance); // [Function: distance] copy

Если загружаемый с помощью require() модуль содержит верхнеуровневый await или граф модулей, который он import, содержит верхнеуровневый await, будет выброшена ошибка ERR_REQUIRE_ASYNC_MODULE. В этом случае пользователям следует загружать асинхронный модуль с помощью import().

Если включен флаг --experimental-print-required-tla, вместо выброса ошибки ERR_REQUIRE_ASYNC_MODULE перед вычислением Node.js выполнит модуль, попытается найти верхнеуровневые await и выведет их местоположение, чтобы помочь пользователям исправить их.

Поддержка загрузки модулей ES с помощью require() в настоящее время является экспериментальной и может быть отключена с помощью --no-experimental-require-module. Чтобы вывести места, где используется эта функция, используйте --trace-require-module.

Наличие этой функции можно проверить, убедившись, что process.features.require_module имеет значение true.

Все вместе

Чтобы получить точное имя файла, который будет загружен при вызове require(), используйте функцию require.resolve().

Обобщая все вышесказанное, приведем высокоуровневый алгоритм на псевдокоде того, что делает require():

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with '/'
   a. set Y to the file system root
3. If X is equal to '.', or X begins with './', '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
   c. THROW "not found"
4. If X begins with '#'
   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. THROW "not found"

MAYBE_DETECT_AND_LOAD(X)
1. If X parses as a CommonJS module, load X as a CommonJS module. STOP.
2. Else, if the source code of X can be parsed as ECMAScript module using
  <a href="esm.md#resolver-algorithm-specification">DETECT_MODULE_SYNTAX defined in
  the ESM resolver</a>,
  a. Load X as an ECMAScript module. STOP.
3. THROW the SyntaxError from attempting to parse X as CommonJS in 1. STOP.

LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
2. If X.js is a file,
    a. Find the closest package scope SCOPE to X.
    b. If no scope was found
      1. MAYBE_DETECT_AND_LOAD(X.js)
    c. If the SCOPE/package.json contains "type" field,
      1. If the "type" field is "module", load X.js as an ECMAScript module. STOP.
      2. If the "type" field is "commonjs", load X.js as a CommonJS module. STOP.
    d. MAYBE_DETECT_AND_LOAD(X.js)
3. If X.json is a file, load X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP

LOAD_INDEX(X)
1. If X/index.js is a file
    a. Find the closest package scope SCOPE to X.
    b. If no scope was found, load X/index.js as a CommonJS module. STOP.
    c. If the SCOPE/package.json contains "type" field,
      1. If the "type" field is "module", load X/index.js as an ECMAScript module. STOP.
      2. Else, load X/index.js as a CommonJS module. STOP.
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. If "main" is a falsy value, GOTO 2.
   c. let M = X + (json main field)
   d. LOAD_AS_FILE(M)
   e. LOAD_INDEX(M)
   f. LOAD_INDEX(X) DEPRECATED
   g. THROW "not found"
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules", GOTO d.
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS + GLOBAL_FOLDERS

LOAD_PACKAGE_IMPORTS(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "imports" is null or undefined, return.
4. If `--experimental-require-module` is enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
5. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
  CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
6. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(X, DIR)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
   may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. If X does not match this pattern or DIR/NAME/package.json is not a file,
   return.
3. Parse DIR/NAME/package.json, and look for "exports" field.
4. If "exports" is null or undefined, return.
5. If `--experimental-require-module` is enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
6. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
   `package.json` "exports", CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
7. RESOLVE_ESM_MATCH(MATCH)

LOAD_PACKAGE_SELF(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "exports" is null or undefined, return.
4. If the SCOPE/package.json "name" is not the first segment of X, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
   "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
   <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
6. RESOLVE_ESM_MATCH(MATCH)

RESOLVE_ESM_MATCH(MATCH)
1. let RESOLVED_PATH = fileURLToPath(MATCH)
2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
   format. STOP
3. THROW "not found" copy

Кэширование

Модули кэшируются после первой загрузки. Это означает (помимо прочего), что каждый вызов require('foo') вернет абсолютно один и тот же объект, если он разрешается в один и тот же файл.

Если require.cache не изменяется, многократные вызовы require('foo') не приведут к многократному выполнению кода модуля. Это важная особенность. Благодаря ей могут возвращаться «частично готовые» объекты, что позволяет загружать транзитивные зависимости, даже если они образуют циклы.

Чтобы модуль выполнял код несколько раз, экспортируйте функцию и вызывайте ее.

Особенности кэширования модулей

Модули кэшируются на основе их разрешенного имени файла. Поскольку модули могут разрешаться в разные имена файлов в зависимости от расположения вызывающего модуля (загрузка из папок node_modules), нет гарантии того, что require('foo') всегда вернет один и тот же объект, если он разрешается в разные файлы.

Кроме того, в нечувствительных к регистру файловых системах или операционных системах разные разрешенные имена файлов могут указывать на один и тот же файл, но кэш все равно будет рассматривать их как разные модули и перезагружать файл несколько раз. Например, require('./foo') и require('./FOO') возвращают два разных объекта независимо от того, являются ли ./foo и ./FOO одним и тем же файлом.

Встроенные модули

История изменений
Версия Изменения
v16.0.0, v14.18.0

Добавлена поддержка импорта node: в require(...).

Node.js имеет несколько модулей, скомпилированных в бинарный файл. Эти модули более подробно описаны в других разделах этой документации.

Встроенные модули определены в исходном коде Node.js и расположены в папке lib/.

Встроенные модули можно идентифицировать с помощью префикса node:, и в этом случае обходится кэш require. Например, require('node:http') всегда вернет встроенный модуль HTTP, даже если существует запись require.cache с таким именем.

Некоторые встроенные модули всегда загружаются преимущественно, если их идентификатор передан в require(). Например, require('http') всегда вернет встроенный модуль HTTP, даже если существует файл с таким именем.

Список всех встроенных модулей можно получить из module.builtinModules. Все модули перечислены без префикса node:, за исключением тех, для которых такой префикс обязателен (как описано в следующем разделе).

Встроенные модули с обязательным префиксом node:

При загрузке с помощью require() некоторые встроенные модули должны запрашиваться с префиксом node:. Это требование существует для того, чтобы предотвратить конфликт новых встроенных модулей с пакетами пользовательского пространства, которые уже заняли это имя. В настоящее время встроенными модулями, требующими префикс node:, являются:

  • node:sea
  • node:sqlite
  • node:test
  • node:test/reporters

Список этих модулей представлен в module.builtinModules, включая префикс.

Циклические зависимости

При наличии циклических вызовов require() выполнение модуля может быть еще не завершено на момент его возврата.

Рассмотрим следующую ситуацию:

a.js:

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done'); copy

b.js:

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done'); copy

main.js:

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done); copy

Когда main.js загружает a.js, модуль a.js, в свою очередь, загружает b.js. В этот момент b.js пытается загрузить a.js. Чтобы предотвратить бесконечный цикл, незавершенная копия объекта экспорта a.js возвращается в модуль b.js. Затем b.js завершает загрузку, и его объект exports предоставляется модулю a.js.

К моменту, когда main.js загрузит оба модуля, они оба завершат выполнение. Таким образом, вывод этой программы будет следующим:

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true copy

Для правильной работы циклических зависимостей модулей в приложении требуется тщательное планирование.

Файловые модули

Если точное имя файла не найдено, Node.js попытается загрузить требуемое имя файла с добавленными расширениями: .js, .json и, наконец, .node. При загрузке файла с другим расширением (например, .cjs) в require() должно быть передано его полное имя, включая расширение файла (например, require('./file.cjs')).

Файлы .json анализируются как текстовые файлы JSON, файлы .node интерпретируются как скомпилированные модули-аддоны, загружаемые с помощью process.dlopen(). Файлы с любым другим расширением (или вовсе без расширения) анализируются как текстовые файлы JavaScript. Обратитесь к разделу Определение системы модулей, чтобы понять, какая цель синтаксического анализа будет использоваться.

Подключаемый модуль с префиксом '/' указывает на абсолютный путь к файлу. Например, require('/home/marco/foo.js') загрузит файл по пути /home/marco/foo.js.

Подключаемый модуль с префиксом './' указывает относительный путь относительно файла, вызывающего require(). То есть circle.js должен находиться в том же каталоге, что и foo.js, чтобы require('./circle') нашел его.

Без начального '/', './' или '../', указывающего на файл, модуль должен быть либо встроенным модулем, либо загружаться из папки node_modules.

Если указанный путь не существует, require() выбросит ошибку MODULE_NOT_FOUND.

Папки как модули

Стабильность: 3 - Устарело: используйте вместо этого экспорт подпутей или импорт подпутей.

Существует три способа передачи папки в require() в качестве аргумента.

Первый способ — создать файл package.json в корне папки, который указывает модуль main. Пример файла package.json может выглядеть следующим образом:

{ "name" : "some-library",
  "main" : "./lib/some-library.js" } copy

Если бы он находился в папке ./some-library, то require('./some-library') попытался бы загрузить ./some-library/lib/some-library.js.

Если в каталоге нет файла package.json или если запись "main" отсутствует или не может быть разрешена, Node.js попытается загрузить файл index.js или index.node из этого каталога. Например, если бы в предыдущем примере не было файла package.json, то require('./some-library') попытался бы загрузить:

  • ./some-library/index.js
  • ./some-library/index.node

Если эти попытки завершатся неудачно, Node.js сообщит об отсутствии модуля стандартной ошибкой:

Error: Cannot find module 'some-library' copy

Во всех трех приведенных выше случаях вызов import('./some-library') приведет к ошибке ERR_UNSUPPORTED_DIR_IMPORT. Использование экспорта подпутей или импорта подпутей пакета может предоставить те же преимущества структурирования и изоляции, что и папки в качестве модулей, и работает как для require, так и для import.

Загрузка из папок node_modules

Если идентификатор модуля, переданный в require(), не является встроенным модулем и не начинается с '/', '../' или './', Node.js начинает с каталога текущего модуля, добавляет /node_modules и пытается загрузить модуль из этого расположения. Node.js не будет добавлять node_modules к пути, который уже заканчивается на node_modules.

Если модуль там не найден, поиск переходит в родительский каталог и так далее, пока не будет достигнут корень файловой системы.

Например, если файл '/home/ry/projects/foo.js' вызвал require('bar.js'), Node.js будет искать модуль в следующих местах и в таком порядке:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

Это позволяет программам локализовать свои зависимости, чтобы они не конфликтовали.

Можно подключать конкретные файлы или подмодули, поставляемые с модулем, указав суффикс пути после имени модуля. Например, require('example-module/path/to/file') разрешит path/to/file относительно расположения example-module. Путь с суффиксом следует той же семантике разрешения модулей.

Загрузка из глобальных папок

Если переменная окружения NODE_PATH установлена в виде списка абсолютных путей, разделенных двоеточиями, Node.js будет искать модули по этим путям, если они не найдены в других местах.

В Windows пути в NODE_PATH разделяются точкой с запятой (;) вместо двоеточий.

Переменная NODE_PATH изначально создавалась для поддержки загрузки модулей из различных путей до того, как был определен текущий алгоритм разрешения модулей.

Переменная NODE_PATH все еще поддерживается, но сейчас менее необходима, так как в экосистеме Node.js сформировались соглашения по размещению зависимых модулей. Иногда в средах развертывания, использующих NODE_PATH, возникает неожиданное поведение, если разработчики не знают о необходимости установки NODE_PATH. Иногда зависимости модуля меняются, из-за чего при поиске по NODE_PATH загружается другая версия (или даже другой модуль).

Кроме того, Node.js выполняет поиск в следующем списке GLOBAL_FOLDERS:

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

Где $HOME — это домашний каталог пользователя, а $PREFIX — это настроенный для Node.js node_prefix.

Они оставлены в основном по историческим причинам.

Настоятельно рекомендуется размещать зависимости в локальной папке node_modules. Они будут загружаться быстрее и надежнее.

Обертка модуля

Перед выполнением кода модуля Node.js оборачивает его в функцию-обертку следующего вида:

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
}); copy

Благодаря этому Node.js решает несколько задач:

  • Сохраняет переменные верхнего уровня (объявленные с помощью var, const или let) в области видимости модуля, а не глобального объекта.
  • Помогает предоставлять некоторые переменные, выглядящие как глобальные, но фактически относящиеся к конкретному модулю, такие как:
    • Объекты module и exports, которые разработчик может использовать для экспорта значений из модуля.
    • Удобные переменные __filename и __dirname, содержащие абсолютное имя файла модуля и путь к каталогу.

Область видимости модуля

__dirname

Добавлено в: v0.1.27
  • Тип: <string>

Имя каталога текущего модуля. Это то же самое, что и path.dirname() от __filename.

Пример: запуск node example.js из /Users/mjr

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr copy

__filename

Добавлено в: v0.0.1
  • Тип: <string>

Имя файла текущего модуля. Это абсолютный путь к файлу текущего модуля с разрешёнными символическими ссылками.

Для основной программы это не обязательно то же имя файла, что использовалось в командной строке.

Имя каталога текущего модуля см. в __dirname.

Примеры:

Запуск node example.js из /Users/mjr

console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr copy

Даны два модуля: a и b, где b является зависимостью a, и существует следующая структура каталогов:

  • /Users/mjr/app/a.js
  • /Users/mjr/app/node_modules/b/b.js

Обращения к __filename внутри b.js вернут /Users/mjr/app/node_modules/b/b.js, в то время как обращения к __filename внутри a.js вернут /Users/mjr/app/a.js.

exports

Добавлено в: v0.1.12
  • Тип: <Object>

Ссылка на module.exports, которую быстрее набирать. Подробнее о том, когда использовать exports, а когда — module.exports, см. в разделе сокращение для exports.

module

Добавлено в: v0.1.16
  • Тип: <module>

Ссылка на текущий модуль, см. раздел об объекте module. В частности, module.exports используется для определения того, что модуль экспортирует и делает доступным через require().

require(id)

Добавлено в: v0.1.13
  • id <string> имя модуля или путь
  • Возвращает: <any> экспортированное содержимое модуля

Используется для импорта модулей, JSON и локальных файлов. Модули могут быть импортированы из node_modules. Локальные модули и файлы JSON могут быть импортированы с использованием относительного пути (например, ./, ./foo, ./bar/baz, ../foo), который будет разрешаться относительно каталога, указанного в __dirname (если определён), или текущего рабочего каталога. Относительные пути в стиле POSIX разрешаются независимо от операционной системы, то есть приведённые выше примеры будут работать в Windows так же, как и в системах Unix.

// Importing a local module with a path relative to the `__dirname` or current
// working directory. (On Windows, this would resolve to .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule');

// Importing a JSON file:
const jsonData = require('./path/filename.json');

// Importing a module from node_modules or Node.js built-in module:
const crypto = require('node:crypto'); copy
require.cache
Добавлено в: v0.3.0
  • Тип: <Object>

Модули кэшируются в этом объекте при их подключении. Удаление значения ключа из этого объекта приведёт к тому, что следующий вызов require перезагрузит модуль. Это не относится к нативным аддонам, для которых повторная загрузка приведёт к ошибке.

Также возможно добавление или замена записей. Этот кэш проверяется до встроенных модулей, и если имя, совпадающее со встроенным модулем, будет добавлено в кэш, только вызовы require с префиксом node: получат встроенный модуль. Используйте с осторожностью!

const assert = require('node:assert');
const realFs = require('node:fs');

const fakeFs = {};
require.cache.fs = { exports: fakeFs };

assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs); copy
require.extensions
Добавлено в: v0.3.0Устарело с: v0.10.6
Стабильность: 0 — Устарело
  • Тип: <Object>

Указывает require, как обрабатывать определённые расширения файлов.

Обработка файлов с расширением .sjs как .js:

require.extensions['.sjs'] = require.extensions['.js']; copy

Устарело. В прошлом этот список использовался для загрузки модулей, написанных не на JavaScript, в Node.js путём их компиляции по требованию. Однако на практике существуют гораздо более эффективные способы сделать это, такие как загрузка модулей через другую программу Node.js или предварительная компиляция их в JavaScript.

Избегайте использования require.extensions. Его использование может привести к трудноуловимым ошибкам, а разрешение расширений замедляется с каждым зарегистрированным расширением.

require.main
Добавлено в: v0.1.17
  • Тип: <module> | <undefined>

Объект Module, представляющий входной скрипт, загруженный при запуске процесса Node.js, или undefined, если точка входа программы не является модулем CommonJS. См. «Доступ к главному модулю».

В скрипте entry.js:

console.log(require.main); copy
node entry.js copy
Module {
  id: '.',
  path: '/absolute/path/to',
  exports: {},
  filename: '/absolute/path/to/entry.js',
  loaded: false,
  children: [],
  paths:
   [ '/absolute/path/to/node_modules',
     '/absolute/path/node_modules',
     '/absolute/node_modules',
     '/node_modules' ] } copy
require.resolve(request[, options])
История изменений
Версия Изменения
v8.9.0

Теперь поддерживается параметр paths.

v0.3.0

Добавлено в: v0.3.0

  • request <string> Путь к модулю для разрешения.
  • options <Object>
    • paths <string[]> Пути, относительно которых разрешается расположение модуля. Если указаны, эти пути используются вместо путей разрешения по умолчанию, за исключением GLOBAL_FOLDERS, таких как $HOME/.node_modules, которые включаются всегда. Каждый из этих путей используется в качестве отправной точки для алгоритма разрешения модулей, то есть иерархия node_modules проверяется из этого расположения.
  • Возвращает: <string>

Использует внутренний механизм require() для поиска расположения модуля, но вместо загрузки модуля просто возвращает разрешённое имя файла.

Если модуль не найден, возникает ошибка MODULE_NOT_FOUND.

require.resolve.paths(request)
Добавлено в: v8.9.0
  • request <string> Путь к модулю, пути поиска которого извлекаются.
  • Возвращает: <string[]> | <null>

Возвращает массив, содержащий пути, по которым производился поиск при разрешении request, или null, если строка request ссылается на встроенный модуль, например http или fs.

Объект module

Добавлено в: v0.1.16
  • Тип: <Object>

В каждом модуле свободная переменная module является ссылкой на объект, представляющий текущий модуль. Для удобства module.exports также доступен через глобальную для модуля переменную exports. module на самом деле не является глобальной переменной, а локальна для каждого модуля.

module.children

Добавлено в: v0.1.16
  • Тип: <module[]>

Объекты модулей, впервые запрошенные данным модулем.

module.exports

Добавлено в: v0.1.16
  • Тип: <Object>

Объект module.exports создаётся системой Module. Иногда это не подходит; многие хотят, чтобы их модуль был экземпляром некоторого класса. Для этого присвойте нужный объект экспорта свойству module.exports. Присвоение нужного объекта переменной exports просто переопределит локальную переменную exports, что, скорее всего, не то, что требуется.

Например, предположим, что мы создаём модуль с именем a.js:

const EventEmitter = require('node:events');

module.exports = new EventEmitter();

// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
  module.exports.emit('ready');
}, 1000); copy

Затем в другом файле мы можем сделать следующее:

const a = require('./a');
a.on('ready', () => {
  console.log('module "a" is ready');
}); copy

Присваивание module.exports должно выполняться сразу. Его нельзя делать внутри обратных вызовов. Это не работает:

x.js:

setTimeout(() => {
  module.exports = { a: 'hello' };
}, 0); copy

y.js:

const x = require('./x');
console.log(x.a); copy
Сокращение для exports
Добавлено в: v0.1.16

Переменная exports доступна в области видимости уровня файла модуля и получает значение module.exports до вычисления модуля.

Она предоставляет сокращение, позволяя писать exports.f = ... более лаконично вместо module.exports.f = .... Однако обратите внимание, что, как и в случае с любой переменной, если переменной exports присваивается новое значение, она больше не связана с module.exports:

module.exports.hello = true; // Exported from require of module
exports = { hello: false };  // Not exported, only available in the module copy

Когда свойство module.exports полностью заменяется новым объектом, обычно также повторно присваивают exports:

module.exports = exports = function Constructor() {
  // ... etc.
}; copy

Для иллюстрации поведения представьте гипотетическую реализацию require(), которая очень похожа на то, что на самом деле делает require():

function require(/* ... */) {
  const module = { exports: {} };
  ((module, exports) => {
    // Module code here. In this example, define a function.
    function someFunc() {}
    exports = someFunc;
    // At this point, exports is no longer a shortcut to module.exports, and
    // this module will still export an empty default object.
    module.exports = someFunc;
    // At this point, the module will now export someFunc, instead of the
    // default object.
  })(module, module.exports);
  return module.exports;
} copy

module.filename

Добавлено в: v0.1.16
  • Тип: <string>

Полностью разрешённое имя файла модуля.

module.id

Добавлено в: v0.1.16
  • Тип: <string>

Идентификатор модуля. Обычно это полностью разрешённое имя файла.

module.isPreloading

Добавлено в: v15.4.0, v14.17.0
  • Тип: <boolean> true, если модуль выполняется во время фазы предварительной загрузки (preload) Node.js.

module.loaded

Добавлено в: v0.1.16
  • Тип: <boolean>

Завершена ли загрузка модуля или он находится в процессе загрузки.

module.parent

Добавлено в: v0.1.16Устарело с: v14.6.0, v12.19.0
Стабильность: 0 — Устарело: вместо этого используйте require.main и module.children.
  • Тип: <module> | <null> | <undefined>

Модуль, который первым запросил данный модуль через require, или null, если текущий модуль является точкой входа текущего процесса, или undefined, если модуль был загружен тем, что не является модулем CommonJS (например, REPL или import).

module.path

Добавлено в: v11.14.0
  • Тип: <string>

Имя каталога модуля. Обычно совпадает с path.dirname() от module.id.

module.paths

Добавлено в: v0.4.0
  • Тип: <string[]>

Пути поиска модуля.

module.require(id)

Добавлено в: v0.5.1
  • id <string>
  • Возвращает: <any> экспортированное содержимое модуля

Метод module.require() предоставляет возможность загрузить модуль так, как если бы вызов require() производился из исходного модуля.

Для этого необходимо получить ссылку на объект module. Поскольку require() возвращает module.exports, а module обычно доступен только внутри кода конкретного модуля, его необходимо явно экспортировать для использования.

Объект Module

Этот раздел был перемещён в Модули: встроенный модуль module.

  • module.builtinModules
  • module.createRequire(filename)
  • module.syncBuiltinESMExports()

Поддержка Source map v3

Этот раздел был перемещён в Модули: встроенный модуль module.

  • module.findSourceMap(path)
  • Класс: module.SourceMap
    • new SourceMap(payload)
    • sourceMap.payload
    • sourceMap.findEntry(lineNumber, columnNumber)

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v24.x/docs/api/modules.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API