Spec-Zone.ru › Node.js 20 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, и главный модуль недоступен.

Советы разработчикам пакетных менеджеров

Семантика функции require() в Node.js разрабатывалась достаточно общей, чтобы поддерживать разумные структуры каталогов. Программы пакетных менеджеров, такие как 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.

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

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

История изменений
Версия Изменения
v20.19.0

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

v20.19.0

Эта функция больше не требует флага CLI --experimental-require-module.

v20.19.0

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

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, должен избегать зависимости от него.

Когда модуль ES содержит как именованные экспорты, так и экспорт по умолчанию, результатом, возвращаемым 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 begins with './' or '/' 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 = DIR + DIRS
   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, даже если существует файл с таким именем. Список встроенных модулей, которые могут быть загружены без использования префикса node:, представлен как module.builtinModules.

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

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

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

Циклы

При возникновении циклических вызовов 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. Чтобы предотвратить бесконечный цикл, модулю b.js возвращается незавершенная копия объекта экспорта модуля a.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 для подробностей о том, когда использовать exports, а когда — module.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 до того, как модуль будет выполнен.

Она позволяет использовать сокращение, благодаря чему module.exports.f = ... можно записать более лаконично как 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>

Модуль, который первым запросил этот модуль, либо 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-v20.x/docs/api/modules.html

Spec-Zone.ru

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