Контролируем JavaScript импорты с помощью Import maps

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет. С выходом Chrome 89 (а так же в Deno 1.8) появилась возможность использовать Карты импортов (Import maps) – механизма, позволяющего получить контроль над поведением JavaScript-импортов.

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

Если кратко, то теперь можно будет совершенно законно и без всяких сборщиков писать, скажем, так:

import React from 'react';

Под катом разберём как это всё работает.

Для того, чтобы директива import или выражение import() могли разрешать пути к модулям в новом виде, нужно эти пути где-то описать. Да, оказалось никакой магии с подкапотным разрешением зависимостей как в той же Node.js или webpack тут нет.

Карты импортов задаются с помощью тега script с атрибутом type="importmap" в формате JSON.

А теперь на примере. Запускаем статический сервер (например, с помощью python -m SimpleHTTPServer 9000) и создаём два файла:

index.html

<!DOCTYPE html>
<html>
  <body>
    <script type="importmap">
      {
        "imports": {
          "mylib": "./my-lib.mjs"
        }
      }
    </script>
    <script type="module">
      import { sayHi } from "mylib";

      sayHi();
    </script>
  </body>
</html>

и my-lib.mjs

export function sayHi() {
  console.log("hi!");
}

Открываем в браузере страничку, и вуаля: в консоль вывелось "hi!". Далее более подробно разберём, как оно устроено.

Структура

На данный момент, согласно спецификации, описывающий зависимости JSON может содержать два ключа: imports и scopes. Если появится какой-то неизвестный ключ, то должно выводиться предупреждение в консоль (хотя у меня Хром этого не делает).

Imports

Значение ключа imports – объект, содержащий в качестве ключей имена модулей (к которым можно обращаться для последующего импорта) и адрес модуля. Адрес должен начинаться с /, ../, ./ или быть абсолютным URL.

"imports": {
  "module-name": "address"
}

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

Создадим директорию "my-pack" добавив в неё index.mjs с содержимым:

export default function mainFunc() {
  console.log("text from mainFunc");
}

А также в "my-pack" добавим директорию "some-module" с файлом some-helper.mjs с содержимым:

export function someHelper() {
  console.log("text from someHelper");
}

Перепишем importmap нашего index.html:

  <script type="importmap">
    {
      "imports": {
        "mypack": "./my-pack/index.mjs",
        "mypack/": "./my-pack/"
      }
    }
  </script>

Теперь, кроме обычного импорта основного пакета

import mainFunc from "mypack";

мы также можем получить доступ к его внутренним модулям

import { someHelper } from "mypack/some-module/some-helper.mjs";

Scopes

Бывают случаи, когда используя один и тот же импорт (точнее, спецификатор импорта), нам нужно получать разные версии библиотеки в зависимости от того, откуда её импортируют. На этот случай и нужны скоупы. Пример:

<script type="importmap">
  {
    "imports": {
      "mypack": "./my-pack/index.mjs",
      "mypack/": "./my-pack/"
    },
    "scopes": {
      "some/other/url/": {
        "mypack": "./my-pack/index-v2.jsm"
      }
    }
  }
</script>

В данном случае внутри любого модуля, url которого будет начинаться с some/other/url/ импорт "mypack" будет ссылаться на "./my-pack/index-v2.jsm", во всех остальных случаях будет использоваться "./my-pack/index.mjs".

Также есть возможность вложенных скоупов. Например:

<script type="importmap">
{
  "imports": {
    "a": "/a-1.mjs",
    "b": "/b-1.mjs",
    "c": "/c-1.mjs"
  },
  "scopes": {
    "/scope2/": {
      "a": "/a-2.mjs"
    },
    "/scope2/scope3/": {
      "b": "/b-3.mjs"
    }
  }
}
</script>

Это даст нам такое разрешение путей:

Specifier

Referrer

Resulting URL

a

/scope1/foo.mjs

/a-1.mjs

b

/scope1/foo.mjs

/b-1.mjs

c

/scope1/foo.mjs

/c-1.mjs

a

/scope2/foo.mjs

/a-2.mjs

b

/scope2/foo.mjs

/b-1.mjs

c

/scope2/foo.mjs

/c-1.mjs

a

/scope2/scope3/foo.mjs

/a-2.mjs

b

/scope2/scope3/foo.mjs

/b-3.mjs

c

/scope2/scope3/foo.mjs

/c-1.mjs

Подключение карт импортов

Как и с остальными ресурсами, подключаемыми через тег script. Можно заполнять содержимое тега:

<script type="importmap">
{
  "imports": { ... },
  "scopes": { ... }
}
</script>

а можно импортировать карту используя атрибут src:

<script type="importmap" src="some/address/to/import-map.importmap"></script>

Важно, что по этому адресу ответ должен приходить с MIME type application/importmap+json.

Особенности

  1. Карты импортов блокируют остальные запросы импортов, поэтому рекомендуется использовать инлайновый вариант.

  2. Если добавить карту импорта после использования определения модулей, то это приведёт к ошибке:

    An import map is added after module script load was triggered.

  3. На момент написания этой статьи есть возможность добавить только одну карту импорта. Если добавить вторую, то это приведёт к ошибке. В Хроме выводит следующее:

    Multiple import maps are not yet supported. https://crbug.com/927119

Deno

В Deno карты импортов подключаются помощью флага --import-map:

deno run --import-map=import_map.json index.ts

Где import_map.json - это карта импортов, а index.ts - файл для запуска (компиляции).

Источники

https://wicg.github.io/import-maps

https://github.com/WICG/import-maps

https://deno.land/manual/linking_to_external_code/import_maps

Источник: https://habr.com/ru/post/547214/


Интересные статьи

Интересные статьи

Когда мы создаем программное обеспечение, мы хотим создавать «-способности»: понятность, ремонтопригодность, расширяемость и - в тренде сейчас - декомпозицию (чтобы мы могли разложить мон...
Сегодня разберем выступление Джеймса Вуттона из IBM Quantum на конференции FDG 2020. Речь пойдет о квантовых вычислениях — потенциально многообещающей технологии, для которой, одн...
В этом обзоре хочу рассказать о сервисе JReader, над которым работаю. Он предназначен для людей, которые хотят эффективно и удобно читать англоязычные тексты. Предыстория или ...
В далеком 1887 году шотландский физик Уильям Томсон предложил свою геометрическую модель структуры эфира, который якобы являлся всепроникающей средой, колебания которой проявляются для нас ка...
На данный момент LLVM стала уже очень популярной системой, которую многие активно используют для создания различных компиляторов, анализаторов и т.п. Уже написано большое количество полезных мате...