Контролируем 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
и 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:
Теперь, кроме обычного импорта основного пакета
import mainFunc from "mypack";
мы также можем получить доступ к его внутренним модулям
import { someHelper } from "mypack/some-module/some-helper.mjs";
Scopes
Бывают случаи, когда используя один и тот же импорт (точнее, спецификатор импорта), нам нужно получать разные версии библиотеки в зависимости от того, откуда её импортируют. На этот случай и нужны скоупы. Пример:
В данном случае внутри любого модуля, url которого будет начинаться с some/other/url/ импорт «mypack» будет ссылаться на »./my-pack/index-v2.jsm», во всех остальных случаях будет использоваться »./my-pack/index.mjs».
Также есть возможность вложенных скоупов. Например:
Это даст нам такое разрешение путей:
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. Можно заполнять содержимое тега:
а можно импортировать карту используя атрибут src:
Важно, что по этому адресу ответ должен приходить с MIME type application/importmap+json.
Особенности
Карты импортов блокируют остальные запросы импортов, поэтому рекомендуется использовать инлайновый вариант.
Если добавить карту импорта после использования определения модулей, то это приведёт к ошибке:
An import map is added after module script load was triggered.
На момент написания этой статьи есть возможность добавить только одну карту импорта. Если добавить вторую, то это приведёт к ошибке. В Хроме выводит следующее:
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