Модули ECMAScript
Node.js содержит поддержку модулей ES на основе Node.js EP для модулей ES.
Не все функции EP завершены и будут реализованы по мере готовности поддержки VM и реализации. Сообщения об ошибках всё ещё дорабатываются.
Включение
Флаг --experimental-modules может быть использован для включения функций загрузки модулей ESM.
После его установки файлы с расширением .mjs смогут загружаться как модули ES.
node --experimental-modules my-app.mjs
Функциональные возможности
Поддерживаемые
Только аргумент командной строки для основной точки входа в программу может быть точкой входа в граф ESM. Динамический импорт также может использоваться для создания точек входа в графы ESM во время выполнения.
Неподдерживаемые
| Функция | Причина |
|---|---|
require('./foo.mjs') | Модули ES имеют разные правила разрешения и синхронизации, используйте стандарт языка import()
|
import() | ожидается выпуск новой версии V8, используемой в Node.js |
import.meta | ожидается реализация в V8 |
Существенные различия между import и require
Отсутствует NODE_PATH
NODE_PATH не входит в процесс разрешения import спецификаторов. Пожалуйста, используйте символические ссылки, если такое поведение необходимо.
Отсутствует require.extensions
require.extensions не используется import. Ожидается, что в будущем для этого потока работы будут доступны хуки загрузчика.
Отсутствует require.cache
require.cache не используется import. Он имеет отдельный кэш.
Пути, основанные на URL
Модули ESM разрешаются и кэшируются на основе семантики URL. Это означает, что файлы, содержащие специальные символы, такие как # и ? должны быть экранированы.
Модули будут загружаться несколько раз, если спецификатор import , используемый для их разрешения, имеет разные параметры запроса или фрагмента.
import './foo?query=1'; // loads ./foo with query of "?query=1" import './foo?query=2'; // loads ./foo with query of "?query=2"
Пока загружаться могут только модули, использующие протокол file:.
Взаимодействие с существующими модулями
Все модули CommonJS, JSON и C++ могут использоваться с import.
Загруженные таким образом модули будут загружены только один раз, даже если их строка запроса или фрагмента отличается между import операторами.
При загрузке через import эти модули предоставят единственный экспорт default , представляющий значение module.exports на момент завершения их вычисления.
import fs from 'fs';
fs.readFile('./foo.txt', (err, body) => {
if (err) {
console.error(err);
} else {
console.log(body);
}
});
Хуки загрузчика
Для настройки стандартного разрешения модулей можно предоставить хуки загрузчика через аргумент --loader ./loader-name.mjs для Node.
При использовании хуков они применяются только к загрузке модулей ES, а не к модулям CommonJS.
Хук разрешения
Хук разрешения возвращает разрешенный URL файла и формат модуля для заданного спецификатора модуля и родительского URL файла:
import url from 'url';
export async function resolve(specifier, parentModuleURL, defaultResolver) {
return {
url: new URL(specifier, parentModuleURL).href,
format: 'esm'
};
}
Функция стандартного разрешения модулей NodeJS предоставляется в качестве третьего аргумента для разрешителя, для обеспечения простоты взаимодействия совместимости.
Помимо возвращаемого разрешенного URL файла, хук разрешения также возвращает свойство format , указывающее формат модуля разрешенного модуля. Это может быть одно из следующих:
format | Описание |
|---|---|
'esm' | Загрузить стандартный JavaScript модуль |
'commonjs' | Загрузить модуль CommonJS в стиле Node |
'builtin' | Загрузить встроенный модуль CommonJS Node |
'json' | Загрузить файл JSON |
'addon' | Загрузить C++ плагин |
'dynamic' | Использовать динамический хук инициализации |
Например, можно создать загрузчик для загрузки JavaScript, ограниченный правилами разрешения браузера, с поддержкой только расширения JS и встроенных модулей Node:
import url from 'url';
import path from 'path';
import process from 'process';
import Module from 'module';
const builtins = Module.builtinModules;
const JS_EXTENSIONS = new Set(['.js', '.mjs']);
export function resolve(specifier, parentModuleURL/*, defaultResolve */) {
if (builtins.includes(specifier)) {
return {
url: specifier,
format: 'builtin'
};
}
if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) {
// For node_modules support:
// return defaultResolve(specifier, parentModuleURL);
throw new Error(
`imports must begin with '/', './', or '../'; '${specifier}' does not`);
}
const resolved = new url.URL(specifier, parentModuleURL);
const ext = path.extname(resolved.pathname);
if (!JS_EXTENSIONS.has(ext)) {
throw new Error(
`Cannot load file with non-JavaScript file extension ${ext}.`);
}
return {
url: resolved.href,
format: 'esm'
};
}
С этим загрузчиком выполнение:
NODE_OPTIONS='--experimental-modules --loader ./custom-loader.mjs' node x.js
загрузит модуль x.js как модуль ES с поддержкой относительного разрешения (с пропуском загрузки node_modules в данном примере).
Динамический хук инициализации
Для создания пользовательского динамического модуля, который не соответствует одному из существующих format интерпретаций, можно использовать хук dynamicInstantiate. Этот хук вызывается только для модулей, возвращающих format: 'dynamic' из хука resolve.
export async function dynamicInstantiate(url) {
return {
exports: ['customExportName'],
execute: (exports) => {
// get and set functions provided for pre-allocated export names
exports.customExportName.set('value');
}
};
}
С предопределённым списком экспортов модуля, функция execute затем будет вызвана в точности в момент вычисления модуля в порядке импорта для этого модуля в дереве импорта.
© 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-v8.x/docs/api/esm.html