Skip to content

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

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

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

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

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

js
const circle = require('./circle.js')
console.log(`Площадь круга радиусом 4 равна ${circle.area(4)}`)

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

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

js
const { PI } = Math

exports.area = r => PI * r ** 2

exports.circumference = r => 2 * PI * r

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

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

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

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

js
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`Площадь mySquare равна ${mySquare.area()}`)

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

js
// Присваивание exports не изменит модуль, необходимо использовать module.exports
module.exports = class Square {
  constructor(width) {
    this.width = width
  }

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

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

Включение

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

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

  • Файлы с расширением .cjs;
  • Файлы с расширением .js, если ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "commonjs".
  • Файлы с расширением .js или без расширения, если ближайший родительский файл package.json не содержит поля верхнего уровня "type" или отсутствует файл package.json в любой родительской папке; если только файл не содержит синтаксис, который вызывает ошибку, если он не интерпретируется как ES-модуль. Авторы пакетов должны включать поле "type", даже в пакетах, где все источники являются CommonJS. Явное указание типа пакета упростит для инструментов сборки и загрузчиков определение того, как следует интерпретировать файлы в пакете.
  • Файлы с расширением, отличным от .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.

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

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

[История]

ВерсияИзменения
v23.5.0Эта функция больше не выдает предупреждение об экспериментальной функции по умолчанию, хотя предупреждение все еще может быть выведено с помощью --trace-require-module.
v23.0.0Эта функция больше не находится за флагом CLI --experimental-require-module.
v23.0.0Поддержка экспорта 'module.exports' в require(esm).
v22.0.0, v20.17.0Добавлено в: v22.0.0, v20.17.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1.2 - Кандидат на выпуск

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

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

  • Модуль полностью синхронный (не содержит операторов await верхнего уровня); и
  • Выполняется одно из этих условий:

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

С такими ES-модулями:

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

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

js
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,
// }

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

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

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

// `distance` теряется для потребителей CommonJS этого модуля, если он не
// добавлен в `Point` как статическое свойство.
export function distance(a, b) {
  return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export { Point as 'module.exports' }
js
const Point = require('./point.mjs')
console.log(Point) // [class Point]

// Именованные экспорты теряются при использовании 'module.exports'
const { distance } = require('./point.mjs')
console.log(distance) // undefined

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

js
export function distance(a, b) {
  return (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' }
js
const Point = require('./point.mjs')
console.log(Point) // [class Point]

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

Если модуль, который передается в require(), содержит операторы await верхнего уровня, или граф модулей, который он импортирует, содержит операторы 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():

text
require(X) из модуля по пути Y
1. Если X — это базовый модуль,
   a. вернуть базовый модуль
   b. ОСТАНОВИТЬСЯ
2. Если X начинается с '/'
   a. установить Y как корень файловой системы
3. Если X начинается с './' или '/' или '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
   c. ВЫБРОСИТЬ "не найдено"
4. Если X начинается с '#'
   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. ВЫБРОСИТЬ "не найдено"

MAYBE_DETECT_AND_LOAD(X)
1. Если X разбирается как модуль CommonJS, загрузить X как модуль CommonJS. ОСТАНОВИТЬСЯ.
2. Иначе, если исходный код X может быть разобран как модуль ECMAScript, используя
  <a href="esm#resolver-algorithm-specification">DETECT_MODULE_SYNTAX, определённый в
  решателе ESM</a>,
  a. Загрузить X как модуль ECMAScript. ОСТАНОВИТЬСЯ.
3. ВЫБРОСИТЬ SyntaxError из попытки разобрать X как CommonJS в 1. ОСТАНОВИТЬСЯ.

LOAD_AS_FILE(X)
1. Если X — файл, загрузить X в формате его расширения. ОСТАНОВИТЬСЯ
2. Если X.js — файл,
    a. Найти ближайшую область действия пакета SCOPE к X.
    b. Если область действия не найдена
      1. MAYBE_DETECT_AND_LOAD(X.js)
    c. Если SCOPE/package.json содержит поле "type",
      1. Если поле "type" равно "module", загрузить X.js как модуль ECMAScript. ОСТАНОВИТЬСЯ.
      2. Если поле "type" равно "commonjs", загрузить X.js как модуль CommonJS. ОСТАНОВИТЬСЯ.
    d. MAYBE_DETECT_AND_LOAD(X.js)
3. Если X.json — файл, загрузить X.json как JavaScript объект. ОСТАНОВИТЬСЯ
4. Если X.node — файл, загрузить X.node как бинарное дополнение. ОСТАНОВИТЬСЯ

LOAD_INDEX(X)
1. Если X/index.js — файл
    a. Найти ближайшую область действия пакета SCOPE к X.
    b. Если область действия не найдена, загрузить X/index.js как модуль CommonJS. ОСТАНОВИТЬСЯ.
    c. Если SCOPE/package.json содержит поле "type",
      1. Если поле "type" равно "module", загрузить X/index.js как модуль ECMAScript. ОСТАНОВИТЬСЯ.
      2. Иначе, загрузить X/index.js как модуль CommonJS. ОСТАНОВИТЬСЯ.
2. Если X/index.json — файл, разобрать X/index.json в JavaScript объект. ОСТАНОВИТЬСЯ
3. Если X/index.node — файл, загрузить X/index.node как бинарное дополнение. ОСТАНОВИТЬСЯ

LOAD_AS_DIRECTORY(X)
1. Если X/package.json — файл,
   a. Разобрать X/package.json и найти поле "main".
   b. Если "main" — ложное значение, ПЕРЕЙТИ к 2.
   c. пусть M = X + (поле json main)
   d. LOAD_AS_FILE(M)
   e. LOAD_INDEX(M)
   f. LOAD_INDEX(X) УСТАРЕЛО
   g. ВЫБРОСИТЬ "не найдено"
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. пусть DIRS = NODE_MODULES_PATHS(START)
2. для каждого DIR в DIRS:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. пусть PARTS = разделить путь(START)
2. пусть I = количество PARTS - 1
3. пусть DIRS = []
4. пока I >= 0,
   a. если PARTS[I] = "node_modules", ПЕРЕЙТИ к d.
   b. DIR = соединить путь(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIR + DIRS
   d. пусть I = I - 1
5. вернуть DIRS + GLOBAL_FOLDERS

LOAD_PACKAGE_IMPORTS(X, DIR)
1. Найти ближайшую область действия пакета SCOPE к DIR.
2. Если область действия не найдена, вернуть.
3. Если SCOPE/package.json "imports" равно null или undefined, вернуть.
4. Если включён `--experimental-require-module`
  a. пусть CONDITIONS = ["node", "require", "module-sync"]
  b. Иначе, пусть CONDITIONS = ["node", "require"]
5. пусть MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
  CONDITIONS) <a href="esm#resolver-algorithm-specification">определённый в решателе ESM</a>.
6. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(X, DIR)
1. Попытаться интерпретировать X как комбинацию NAME и SUBPATH, где имя
   может иметь префикс @scope/, а subpath начинается с косой черты (`/`).
2. Если X не соответствует этому шаблону или DIR/NAME/package.json не является файлом,
   вернуть.
3. Разобрать DIR/NAME/package.json и найти поле "exports".
4. Если "exports" равно null или undefined, вернуть.
5. Если включён `--experimental-require-module`
  a. пусть CONDITIONS = ["node", "require", "module-sync"]
  b. Иначе, пусть CONDITIONS = ["node", "require"]
6. пусть MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
   `package.json` "exports", CONDITIONS) <a href="esm#resolver-algorithm-specification">определённый в решателе ESM</a>.
7. RESOLVE_ESM_MATCH(MATCH)

LOAD_PACKAGE_SELF(X, DIR)
1. Найти ближайшую область действия пакета SCOPE к DIR.
2. Если область действия не найдена, вернуть.
3. Если SCOPE/package.json "exports" равно null или undefined, вернуть.
4. Если SCOPE/package.json "name" не является первым сегментом X, вернуть.
5. пусть MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
   "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
   <a href="esm#resolver-algorithm-specification">определённый в решателе ESM</a>.
6. RESOLVE_ESM_MATCH(MATCH)

RESOLVE_ESM_MATCH(MATCH)
1. пусть RESOLVED_PATH = fileURLToPath(MATCH)
2. Если файл по пути RESOLVED_PATH существует, загрузить RESOLVED_PATH в формате его расширения. ОСТАНОВИТЬСЯ
3. ВЫБРОСИТЬ "не найдено"

Кэширование

Модули кэшируются после первого обращения. Это означает (помимо прочего), что каждый вызов 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:, это:

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

Циклы

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

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

a.js:

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')

b.js:

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')

main.js:

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)

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

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

bash
$ 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

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

Модули файлов

Если точное имя файла не найдено, 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 - Устарело]

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

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

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

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

Если это находится в папке ./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 сообщит о том, что весь модуль отсутствует, с ошибкой по умолчанию:

bash
Error: Cannot find module 'some-library'

Во всех трех случаях выше вызов 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_prefix Node.js.

Это в основном по историческим причинам.

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

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

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

js
;(function (exports, require, module, __filename, __dirname) {
  // Код модуля находится здесь
})

Таким образом, Node.js достигает нескольких целей:

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

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

__dirname

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

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

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

js
console.log(__dirname)
// Выводит: /Users/mjr
console.log(path.dirname(__filename))
// Выводит: /Users/mjr

__filename

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

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

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

См. __dirname для имени каталога текущего модуля.

Примеры:

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

js
console.log(__filename)
// Выводит: /Users/mjr/example.js
console.log(__dirname)
// Выводит: /Users/mjr

Учитывая два модуля: 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

Ссылка на module.exports, которая короче для набора текста. См. раздел о сокращении exports для получения подробной информации о том, когда использовать exports и когда использовать module.exports.

module

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

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

require(id)

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

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

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

js
// Импорт локального модуля с путем, относительным к `__dirname` или текущему
// рабочему каталогу. (В Windows это будет соответствовать .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule')

// Импорт JSON-файла:
const jsonData = require('./path/filename.json')

// Импорт модуля из node_modules или встроенного модуля Node.js:
const crypto = require('node:crypto')

require.cache

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

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

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

js
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)

require.extensions

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

Устарел с: v0.10.6

[Стабильность: 0 - Устарело]

Стабильность: 0 Стабильность: 0 - Устарело

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

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

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

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

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

require.main

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

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

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

js
console.log(require.main)
bash
node entry.js
js
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' ] }

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

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

module.children

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

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

module.exports

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

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

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

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

module.exports = new EventEmitter()

// Выполняем некоторую работу и через некоторое время генерируем
// событие 'ready' из самого модуля.
setTimeout(() => {
  module.exports.emit('ready')
}, 1000)

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

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

Присваивание module.exports должно быть выполнено немедленно. Это не может быть сделано в каких-либо обратных вызовах. Это не работает:

x.js:

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

y.js:

js
const x = require('./x')
console.log(x.a)

Сокращение exports

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

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

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

js
module.exports.hello = true // Экспортируется из require модуля
exports = { hello: false } // Не экспортируется, доступно только в модуле

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

js
module.exports = exports = function Constructor() {
  // ... и так далее
}

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

js
function require(/* ... */) {
  const module = { exports: {} }
  ;((module, exports) => {
    // Код модуля здесь. В этом примере определяется функция.
    function someFunc() {}
    exports = someFunc
    // На этом этапе exports больше не является сокращением для module.exports, и
    // этот модуль будет по-прежнему экспортировать пустой объект по умолчанию.
    module.exports = someFunc
    // На этом этапе модуль будет теперь экспортировать someFunc, а не
    // объект по умолчанию.
  })(module, module.exports)
  return module.exports
}

module.filename

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

Полное разрешенное имя файла модуля.

module.id

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

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

module.isPreloading

Добавлено в: v15.4.0, v14.17.0

  • Тип: <булево> true, если модуль выполняется во время фазы предварительной загрузки Node.js.

module.loaded

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

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

module.parent

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

Устарел с: v14.6.0, v12.19.0

[Стабильность: 0 - Устарел]

Стабильность: 0 Стабильность: 0 - Устарел: Пожалуйста, используйте require.main и module.children вместо этого.

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

module.path

Добавлен в: v11.14.0

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

module.paths

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

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

module.require(id)

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

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

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

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

Объект Module

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

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

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