Хабрахабр

[Перевод] Анонсируем поддержку ECMAScript модулей в Node.js

2. Node.js 13. Ранее эта функциональность была за флагом --experimental-modules, который больше не требуется. 0 идет с поддержкой ECMAScript модулей, известных по своему синтаксису import и export. Однако, реализация все еще экспериментальная и может меняться.

От переводчика: это долгожданная фича наконец-то позволит нам использовать стандартный модульный синтаксис, уже доступный в современных браузерах, а теперь еще и в Node.js без флагов и транспайлеров

Активация

Node.js будет обрабатывать код как ES-модули в следующих случаях:

  • Файлы с расширением .mjs
  • Файлы с расширением .js или без расширения вообще, при условии что ближайший к ним родительский package.json содержит значение "type": "module"
  • Код, переданный через аргумент —-eval или STDIN, вместе с флагом —-input-type=module

Это относится к .js файлам без "type": "module" в ближайшем package.json и коду, переданному через командную строку без указания --input-type. Во всех остальных случаях код будет считаться CommonJS. Однако, поскольку у нас теперь есть два вида модулей, CommonJS и ES, будет лучше указывать тип модулей явным образом. Это сделано в целях сохранения обратной совместимости.

Вы можете явно отметить свой код как CommonJS следующими признаками:

  • Файлы с расширением .cjs
  • Файлы с расширением .js или без расширения вообще, при условии что ближайший родительский package.json содержит значение "type": "“commonjs”"
  • Код, переданный через аргумент --eval или STDIN c явным флагом --input-type=commonjs

Чтобы узнать больше об этих фичах, смотрите разделы документации "Package Scope and File Extensions" и про флаг --input-type

Синтаксис import и export

Они могут быть указаны одним из следующих форматов: В контексте ES модуля, можно использовать import, указывающий на другие Javascript файлы.

  • Относительный URL: "./file.mjs"
  • Абсолютный URL c file://, например "file:///opt/app/file.mjs"
  • Имя пакета: "es-module-package"
  • Путь до файла внутри пакета: "es-module-package/lib/file.mjs"

Все встроенные Node.js пакеты, вроде fs или path, поддерживают все три вида импортов. В импортах можно использовать дефолтные (import _ from "es-module-package") и именованные значения (import from "es-module-package"), а также импортировать всё как один неймспейс (import * as fs from "fs").

Импорты, которые указывают на CommonJS код (то есть весь текущий JavaScript, написанный для Node.js с использованием require и module.exports), могут использовать только дефолтный вариант (import _ from "commonjs-package").

Тем не менее, вы можете загружать эти файлы используя module.createRequire API, который доступен без дополнительных флагов. Импорт других форматов файлов, таких как JSON и WASM остается экспериментальным, и требует флагов --experimental-json-modules и --experimental-wasm-modules соответственно.

В своих ES-модулях вы можете использовать ключевое слово export для экспортирования дефолтных и именованных значений.

Заметьте, что import() возвращает не модуль а его обещание (Promise). Динамический выражения с import() могут использоваться для загрузки ES-модулей либо из CommonJS, либо ES кода.

Также в модулях доступен import.meta.url, который содержит абсолютный URL текущего ES-модуля.

Файлы и новое поле "type" в package.json

Добавьте "type": "module" в package.json своего проекта, и Node.js начнет воспринимать все .js-файлы вашего проекта как ES модули.

Если какие-то файлы вашего проекта все еще используют CommonJS и у вас не получается мигрировать весь проект разом, вы можете либо использовать расширение .cjs для этого кода, либо сложить его в отдельную директорию, и добавить туда package.json, содержащий { "type": "commonjs" }, который укажет Node.js, что она должна обрабатываться как CommonJS.

Этот механизм похож на то, как работает Babel с .babelrc файлами. Для каждого загружаемого файла Node.js посмотрит на package.json в содержащей его директории, затем на уровень выше, и так до тех пор пока не достигнет корневой директории. Такой подход позволяет Node.js использовать package.json как источник различных метаданных о пакете и конфигурации, наподобие того, как это уже работает в Babel и других инструментах.

Мы рекомендуем всем разработчикам пакетов указывать поле type, даже если там будет написано commonjs.

Входные точки пакета и поле "exports" в package.json

Поле main поддерживается всеми версиями Node.js, но его возможности ограничены: с ним можно определить только одну главную входную точку в пакет. Теперь у нас есть два поля для указания входной точки в пакет: main и exports. Это дает дополнительную инкапсуляцию для пакетов, где только явно указанные в exports пути доступны для импорта снаружи пакета. Новое поле exports также позволяет определить главную входную точку, а также дополнительные пути. exports применяется к обоим типам модулей, CommonJS и ES, неважно, используются они через require или import.

Также, Node.js выбросит ошибку, если импорт ссылается на pkg/esm/feature.js который не указан в exports. Эта функциональность позволит импортам вида pkg/feature указывать на реальный путь вроде ./node_modules/pkg/esm/feature.js.

Это позволит пакету отдавать CommonJS код для вызова require("pkg") и ES модульный код для импорта через import "pkg", хотя написание такого пакета не лишено других проблем. Дополнительная, все еще экспериментальная, возможность, условные экспорты предоставляет возможность экспортировать разные файлы для различных окружений. Вы можете включить условные экспорты флагом —-experimental-conditional-exports.

Основные грабли новых модулей

Обязательное указание расширений файлов

При импорте индексного файла из директории также нужно полностью указывать путь до файла, то есть "./startup/index.js". При использовании импортов, нужно обязательно указывать расширение файла.

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

Больше недоступны require, exports, module.exports, __filename, __dirname

Однако, require можно импортировать в ES модуль через module.createRequire(). Эти значения из CommonJS недоступны в контексте ES модулей. Эквиваленты __filename и __dirname можно достать из import.meta.url.

Создание пакетов

Рабочая группа по модулям для Node.js все продолжает искать способы улучшить поддержку двойных пакетов, с CommonJS для легаси пользователей и ES модулями для новых. На данный момент мы рекомендуем авторам пакетов использовать либо полностью CommonJS, либо полностью ES модули для своих Node.js проектов. Условные экспорты, сейчас являются экспериментальными и мы надеемся выкатить эту функциональность или ее альтернативу к концу января 2020 года, или даже раньше.

Чтобы узнать об этом больше, смотрите наши примеры и рекомендации по созданию двойных CommonJS/ES Module пакетов

Что будет дальше

Экспериментальное API, доступное под флагом —-experimental-loader, будет подвержено значительным переделкам до того, как мы выведем его из-под флага. Загрузчики. Продолжается работа над API для написания кастомных загрузчиков, для реализации транспиляции модулей в рантайме, переопределения путей импортов (пакетов или отдельных файлов), а также инструментирование кода.

Больше информации об этом у нас в документации. Двойные CommonJS/ES module пакеты. Мы хотим предоставить стандартный способ опубликовать пакет, который может использоваться и через require в CommonJS и через import в ES модулях. Мы планируем завершить работу и вывести из-под флага к концу января 2020 года, если не раньше.

Мы надеемся, поддержка ECMAScript модулей приблизит Node.js к стандартам JavaScript и принесет новые возможности для совместимости во всей экосистеме JavaScript. Вот и всё! Рабочий процесс над улучшением поддержки модулей ведется публично здесь: https://github.com/nodejs/modules.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть