ECMAScript-модули в Node.js: новый план

http://2ality.com/2018/12/nodejs-esm-phases.html
  • Перевод

Текущий статус поддержки ECMAScript-модулей (ESM) в Node.js:


  • Экспериментальная поддержка ESM была добавлена в Node.js 8.5.0 12 сентября 2017 года.
  • После этого Технический Руководящий Комитет Node.js сформировал команду, ответственную за модули (Modules Team), чтобы она помогла спроектировать недостающие части для грядущего (не экспериментального) релиза. Эта команда состоит из людей из различных отраслей веб-разработки (фронтенд, бекенд, JS-движки, и т.д.).

В октябре Modules Team опубликовала "План по реализации Новых Модулей". Этот пост объясняет, что в нем содержится.


Фазы


Процесс разделен на три фазы:


  • Фаза 1: создать "минимальное" ядро – основной набор правил и возможностей, минимальных и бесспорных, насколько это возможно.
  • Фаза 2 и далее: создание на основе ядра более сложной функциональности.

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


Фаза 1: минимальное ядро поддержки ESM в Node.js


Упрощение идентификаторов модулей


Одна из целей Modules Team это достижение "браузерной эквивалентности": Node.js должна быть близка по поведению к браузерам, насколько это возможно. Ядро достигает этого путем упрощения разбора идентификаторов модулей (URL-ов, указывающих на модули):


  • Каждый идентификатор модуля должен заканчиваться именем файла с расширением. То есть
    • Расширения не добавляются автоматически
    • Импортирование директорий не поддерживается (ни через перенаправление на dir/index.mjs, ни чтение поля main в package.json).
  • ES modules могут импортировать встроенные Node.js-модули (path и подобные). Они являются единственным исключением из предыдущего правила.
  • По умолчанию поддерживаются только файлы с расширением .mjs (смотрите Фазу 2, если вам интересен статус других расширений). Таким образом, другие виды модулей не смогут быть импортированы через import: модули CommonJS, JSON-файлы, нативные модули.

Принесение важных возможностей CommonJS в ES-модули


  • URL текущего модуля (аналогично __filename из CommonJS): import.meta.url
  • Динамический импорт ES-модулей (доступен через require() в CommonJS): оператор import()

Совместимость


  • ES-модули смогут импортировать CommonJS-модули через createRequireFromPath(). Это будет работать следующим образом (есть планы сделать сокращенный способ, например, функцию createRequireFromUrl()):

import {createRequireFromPath as createRequire} from 'module';
import {fileURLToPath as fromPath} from 'url';
const require = createRequire(fromPath(import.meta.url));

const cjsModule = require('./cjs-module.js');

  • CommonJS-модули смогут загружать ES-модули через import().

Фаза 2 и дальнейшие планы


  • Во второй фазе нас ждет:
    • Поддержка "bare" индентификаторов, таких как 'lodash'. Скорее всего, это включит в себя некоторый способ маппинга этих идентификаторов на реальные пути.
    • Поддержка других расширений файлов, помимо .mjs. Это включает в том числе и поддержку ES-модулей в .js файлах.
  • Фаза 3, скорее всего, сосредоточится на загрузчиках модулей с точками расширения, в которых пользователи смогут подключить свою логику.

Когда я смогу пользоваться ES-модулями в Node.js?


  • За флагом: доступно уже сейчас
    • Внимание: поведение еще не соответствует описанному выше в фазе 1 (по состоянию на Node.js 11.5.0)
  • Без флага и в соотвествии с фазой 1: Modules Team старается сделать это возможным как можно скорее. Надеемся, что модули выпустят из под флага в Node.js 14 (апрель 2020 года) и портируют в предыдущие версии, если это будет возможно.

Часто задаваемые вопросы


  • Зачем нужно новое расширение файлов .mjs?
    • Каждое решение по различению форматов ESM и CommonJS имеет свои преимущества и недостатки. Использование отдельного разрешения кажется неплохим вариантом (больше информации).
  • Почему поведение Node.js должно быть похожим на браузерное?
    • Потому что это делает возможным переиспользование кода. Например, чтобы создавать библиотеки, которые работают одновременно и в браузерах, и в Node.js
    • Также это должно облегчить переключение между бекендом и фронтендом во время кодинга.
  • К чему все эти ограничения в целях совместимости?
    • Между ES и CommonJS-модулями есть довольно сильные отличия в структуре (статическая против динамической) и способе загрузки (асинхронная против синхронной). Ограничения помогают сохранить порядок вещей простым – учитывая, что в долгосрочной перспективе подавляющим большинством будут ES-модули.
  • Почему это все тянется так долго?
    • Здесь участвует много заинтересованных сторон и вовлечено много разных платформ (Node.js, npm, браузеры, JS-движки, TypeScript, TC39 и другие). Если мы действительно получим ES-модули, способные работать везде, наверное, это стоит ожидания, ИМХО.

Благодарность


Спасибо Майлсу Боринсу за его отзывы об этом посте.


Дополнительные источники для дальнейшего чтения


Поделиться публикацией

Похожие публикации

Комментарии 8

    0

    Я понял, что я не понимаю, как команда nodejs собирается разделить загрузку commonjs на 2 этапа — чтение интерфейса и выполнение. Одна из затычек, как я понимаю, в этом. Интерфейс es модуля известен до его выполнения, а в commonjs после выполнения.
    И вот как решается этот затык мне не понятно.

      0

      Почему вы решили, что загрузка CommonJS разбивается на 2 этапа?


      С помощью createRequireFromPath вы получаете доступ к обычной синхронной функции require. Она работает как и раньше – синхронно читает файл и возвращает вам значение module.exports из него.

      +1
      import {fileURLToPath as fromPath} from 'url';

      В этом примере алиас неправильный, должен быть toPath. Обратите внимание на название оригинальной функции fileURLToPath. Иначе получается неудачная семантика (я даже запутался сперва):

      import {createRequireFromPath } from 'module';
      
      // сейчас
      const require = createRequireFromPath( fromPath(import.meta.url) );
      // правильно
      const require = createRequireFromPath( toPath(import.meta.url) );
      
        0

        Справедливое замечание. Отправил вопрос автору оригинала, поправлю, если это действительно так.

        0

        А импорт как во фронте уже сделали?

          0
          В браузерах, имеется в виду? Да, уже есть почти во всех: caniuse.com/#feat=es6-module
            0

            Не, в ноде. Насколько мне известно, import и export default там не работает без костылей.

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

      Самое читаемое