NPM — уникальный репозиторий пакетов из мира JavaScript. В основном здесь те JS библиотеки, которые можно использовать во фронтэнде/в браузере, но есть и серверные для использования в node.js и не только. Если вы программируете на Java и у вас появилась необходимость синтегрироваться с NPM репозиторием, то скорее всего у вас один из двух следующих случаев:
- Вы пишите Web приложение на одном из Java фреймворков и определенные NPM пакеты необходимы для клиентской стороны
- У вас Java приложение (например, для Андройда), которому необходимо уметь запрашивать зависимости и сами ресурсы/пакеты из NPM
Давайте посмотрим как это можно сделать в Java.
NPM ресурсы для Web-приложения
У вас есть 2 опции:
- Запаковать нужные NPM ресурсы внутрь вашего WAR/JAR
- Использовать CDN для загрузки в runtime нужных ресурсов
Упаковка NPM ресурсов в WAR/JAR
Прежде всего вам надо узнать побольше о такой штуке как WebJars. Она позволяет «отражать» NPM (и не только) пакеты в репозиторий Maven. Таким образом вы можете работать с NPM пакетами как с обычными Java пакетами в Maven. Например, для того чтобы включить ресурсы из всем известного Boostrap в ваш WAR достаточно добавить следующую зависимость в pom.xml:
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>bootstrap</artifactId>
<version>4.5.0</version>
</dependency>
WebJars отражает пакеты из NPM в Maven вместе со всеми необходимыми зависимостями, так что подключив один JAR по зависимостям будут подключены и все остальные нужные пакеты.
WebJars так же имеет большой набор библиотек под разные Java фреймворки для того чтобы удобнее было работать с запакованными и подключенными ресурсами. Подробнее читайте в документации.
WebJars — отличное подспорье для любого Java Backend разработчика. Но есть и более легкие альтернативы: упаковка нужных пакетов из NPM с помощью Maven плагинов. Здесь, возможно, не полный список:
Например, чтобы включить пакеты vue и vuex нужных версий с помощью jnpm-maven-plugin добавьте следующие строчки в pom.xml:
<plugin>
<groupId>org.orienteer.jnpm</groupId>
<artifactId>jnpm-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>install</goal>
</goals>
<configuration>
<packages>
<package>vue@2.6.11</package>
<package>vuex@~3.4.0</package>
</packages>
</configuration>
</execution>
</executions>
</plugin>
Можно использовать нотацию NPM для определения диапазона нужных версий:
- Звездочка (*|X|x) — 1.* эквивалентно >=1.0.0 & <2.0.0
- Тильда (~) — ~1.5 эквивалентно >=1.5.0 & <1.6.0
- Дефис (-) — 1.0-2.0 эквивалентно >=1.0.0 & <=2.0.0
- Каретка (^) — ^0.2.3 эквивалентно >=0.2.3 & <0.3.0
- Частичный диапазон — 1 эквивалентно 1.X or >=1.0.0 & <2.0.0
- Отрицание — !(1.x) эквивалентно <1.0.0 & >=2.0.0
- Сложные — ~1.3 | (1.4.* & !=1.4.5) | ~2
Также, вы можете задать какие именно файлы нужно включить из пакетов используя includes и excludes. Например, обычно NPM пакет содержит «скомпиленные» файлы в директории /dist. Другие файлы являются исходниками и врядли будут нужны и полезны внутри Java Web приложения. Чтобы включить только содержимое директории dist/ достаточно добавить следующее в секцию :
<includes>
<include>dist/*</include>
</includes>
По умолчанию jnpm-maven-plugin запаковывает ресурсы по точно таким же путям как и WebJars. Это позволяет использовать упомянутые выше библиотеки WebJars для разных фреймворков для доступа к ресурсам. Если вам необходим какой-то другой специфичный формат упаковывания, то просьба обратиться к документации.
Использование CDN
Есть множество публично доступных CDN с NPM ресурсами. Наиболее известные и используемые:
Также вы можете использовать свой собственный CDN (например поднятый через docker) или даже встроить CDN функционал внутрь вашего Web-App. Например, добавьте следующий сервлет в web.xml чтобы включить JNPM CDN. Отредактируйте по необходимости:
<servlet>
<servlet-name>CDNServlet</servlet-name>
<servlet-class>org.orienteer.jnpm.cdn.CDNServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CDNServlet</servlet-name>
<url-pattern>/cdn/*</url-pattern>
</servlet-mapping>
После загрузки сервлета NPM ресурсы будут доступны через URL следующего формата: http(s)://<домен>:<порт>/<путь до веб приложения>/cdn/<NPM пакет>/<путь до файла>.
Например:
localhost:8080/cdn/vue@2.6.11/dist/vue.js
Работа с NPM REST API из Java
Безусловно, вы можете использовать напрямую REST API реестра NPM, скажем, через Retrofit. В этом вам поможет соответствующая документация. Но удобнее использовать библиотеку JNPM, которая представляет Java обертку для данного REST API и не только.
Включаем JNPM Jar в pom.xml:
<dependency>
<groupId>org.orienteer.jnpm</groupId>
<artifactId>jnpm</artifactId>
<version>1.0</version>
</dependency>
Инициализируем JNPM API:
JNPMService.configure(JNPMSettings.builder()
.homeDirectory(Paths.get("/home/myuser/.jnpm")) //Опционально
.downloadDirectory(Paths.get("/tmp")) //Опционально
//Другие возможные опции - см. документацию
.build());
JNPM API предоставляет 2 опции: Синхронное API и Ассинхронное API через RXJava. Что именно использовать — решать вам:
JNPMService jnpmService = JNPMService.instance(); //Synchronous Java API
RxJNPMService rxJnpmService = JNPMService.instance().getRxService() //RXJava API
Пример использования:
//Общая информаци о NPM реестре
System.out.println(JNPMService.instance().getRegistryInfo());
//Заполучить и напечатать информацию о последней версии VUE
System.out.println(JNPMService.instance().getPackageInfo("vue").getLatest());
//Напечатать описание пакета vue@2.6.11
System.out.println(JNPMService.instance().getVersionInfo("vue", "2.6.11").getDescription());
//Напечатать последнюю версию до второго официального релиза
System.out.println(JNPMService.instance().bestMatch("vue@<2").getVersionAsString());
//Скачать архив для vue@2.6.11 и напечатать локальный путь
VersionInfo vueVersion = JNPMService.instance().getVersionInfo("vue", "2.6.11");
vueVersion.downloadTarball().blockingAwait();
System.out.println(vueVersion.getLocalTarball().getAbsolutePath());
//Искать "vue" и напечатать описание первого результата
System.out.println(JNPMService.instance().search("vue").getObjects().get(0).getSearchPackage().getDescription());
// Пройтись и напечатать информацию по всем dev зависимостям последней версии vue
// а так же установить данные пакеты так как делает это сам NPM (node_modules/vue и т.д.)
JNPMService.instance().getRxService()
.traverse(TraverseDirection.WIDER, TraversalRule.DEV_DEPENDENCIES, "vue")
.subscribe(t -> {System.out.println(t); t.install(Paths.get("target", "readme"), InstallationStrategy.NPM);});
Если у вас какой-то специфичный случай, который был не описан здесь — пожалуйста, дайте знать!