Русский
Русский
English
Статистика
Реклама

Npm

Перевод Зачем в npm 7 оставили поддержку package-lock.json?

02.07.2020 16:18:36 | Автор: admin
Мне, с того момента, как мы объявили о том, что в npm 7 будут поддерживаться файлы yarn.lock, несколько раз задавали один и тот же вопрос. Он звучал так: Зачем тогда оставлять поддержку package-lock.json? Почему бы не использовать только yarn.lock?.



Краткий ответ на этот вопрос выглядит так: Потому что yarn.lock не полностью удовлетворяет нуждам npm. Если полагаться исключительно на него, это ухудшит возможности npm по формированию оптимальных схем установки пакетов и возможности по добавлению в проект нового функционала. Ответ более подробный представлен в данном материале.

Базовая структура файла yarn.lock


Файл yarn.lock представляет собой описание соответствия спецификаторов зависимостей пакетов и метаданных, описывающих разрешение этих зависимостей. Например:

mkdirp@1.x:version "1.0.2"resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.2.tgz#5ccd93437619ca7050b538573fc918327eba98fb"integrity sha512-N2REVrJ/X/jGPfit2d7zea2J1pf7EAR5chIUcfHffAZ7gmlam5U65sAm76+o4ntQbSRdTjYf7qZz3chuHlwXEA==

В этом фрагменте сообщается следующее: Любая зависимость от mkdirp@1.x должна разрешаться именно в то, что указано здесь. Если несколько пакетов зависят от mkdirp@1.x, то все эти зависимости будут разрешены одинаково.

В npm 7, если в проекте существует файл yarn.lock, npm будет пользоваться содержащимися в нём метаданными. Значения полей resolved сообщат npm о том, откуда ему нужно загружать пакеты, а значения полей integrity будут использоваться для проверки того, что получено, на предмет соответствия этого тому, что ожидалось получить. Если пакеты добавляются в проект или удаляются из него, соответствующим образом обновляется содержимое yarn.lock.

Npm при этом, как и прежде, создаёт файл package-lock.json. Если в проекте присутствует этот файл, он будет использоваться как авторитетный источник сведений о структуре (форме) дерева зависимостей.

Вопрос тут заключается в следующем: Если yarn.lock достаточно хорош для менеджера пакетов Yarn почему npm не может просто использовать этот файл?.

Детерминированные результаты установки зависимостей


Результаты установки пакетов с помощью Yarn гарантированно будут одними и теми же при использовании одного и того же файла yarn.lock и одной и той же версии Yarn. Применение различных версий Yarn может привести к тому, что файлы пакетов на диске будут расположены по-разному.

Файл yarn.lock гарантирует детерминированное разрешение зависимостей. Например, если foo@1.x разрешается в foo@1.2.3, то, учитывая использование одного и того же файла yarn.lock, это будет происходить всегда, во всех версиях Yarn. Но это (как минимум, само по себе) не эквивалентно гарантии детерминированности структуры дерева зависимостей!

Рассмотрим следующий граф зависимостей:

root -> (foo@1, bar@1)foo -> (baz@1)bar -> (baz@2)

Вот пара схем деревьев зависимостей, каждое из которых можно признать корректным.

Дерево 1:

root+-- foo+-- bar|  +-- baz@2+-- baz@1

Дерево 2:

+-- foo|  +-- baz@1+-- bar+-- baz@2

Файл yarn.lock не может сообщить нам о том, какое именно дерево зависимостей нужно использовать. Если в пакете root будет выполнена команда require(baz) (что некорректно, так как эта зависимость не отражена в дереве зависимостей), файл yarn.lock не гарантирует правильного выполнения этой операции. Это форма детерминизма, которую может дать файл package-lock.json, но не yarn.lock.

На практике, конечно, так как у Yarn, в файле yarn.lock, есть вся информация, необходимая для того чтобы выбрать подходящую версию зависимости, выбор является детерминированным до тех пор, пока все используют одну и ту же версию Yarn. Это означает, что выбор версии всегда делается одним и тем же образом. Код не меняется до тех пор, пока кто-нибудь его не изменит. Надо отметить, что Yarn достаточно интеллектуален для того, чтобы, при создании дерева зависимостей, не зависеть от расхождений, касающихся времени загрузки манифеста пакета. Иначе детерминированность результатов гарантировать было бы нельзя.

Так как это определяется особенностями алгоритмов Yarn, а не структурами данных, имеющимися на диске (не идентифицирующих алгоритм, который будет использован), эта гарантия детерминизма, в своей основе, слабее, чем гарантия, которую даёт package-lock.json, содержащий полное описание структуры дерева зависимостей, хранящегося на диске.

Другими словами, на то, как именно Yarn строит дерево зависимостей, влияют файл yarn.lock и реализация самого Yarn. А в npm на то, каким будет дерево зависимостей, влияет только файл package-lock.json. Благодаря этому структуру проекта, описанную в package-lock.json, становится сложнее случайно нарушить, пользуясь разными версиями npm. А если же в файл будут внесены изменения (может быть по ошибке, или намеренно), эти изменения будут хорошо заметны в файле при добавлении его изменённой версии в репозиторий проекта, в котором используется система контроля версий.

Вложенные зависимости и дедупликация зависимостей


Более того, существует целый класс ситуаций, предусматривающих работу с вложенными зависимостями и дедупликацию зависимостей, когда файл yarn.lock не способен точно отразить результат разрешения зависимостей, который будет, на практике, использоваться npm. Причём, это справедливо даже для тех случаев, когда npm использует yarn.lock в качестве источника метаданных. В то время как npm использует yarn.lock как надёжный источник информации, npm не рассматривает этот файл в роли авторитетного источника сведений об ограничениях, накладываемых на версии зависимостей.

В некоторых случаях Yarn формирует дерево зависимостей с очень высоким уровнем дублирования пакетов, а нам это ни к чему. В результате оказывается, что точное следование алгоритму Yarn в подобных случаях это далеко не идеальное решение.

Рассмотрим следующий граф зависимостей:

root -> (x@1.x, y@1.x, z@1.x)x@1.1.0 -> ()x@1.2.0 -> ()y@1.0.0 -> (x@1.1, z@2.x)z@1.0.0 -> ()z@2.0.0 -> (x@1.x)

Проект root зависит от версий 1.x пакетов x, y и z. Пакет y зависит от x@1.1 и от z@2.x. У пакета z версии 1 нет зависимостей, но этот же пакет версии 2 зависит от x@1.x.

На основе этих сведений npm формирует следующее дерево зависимостей:

root (x@1.x, y@1.x, z@1.x) <-- здесь зависимость x@1.x+-- x 1.2.0        <-- x@1.x разрешается в 1.2.0+-- y (x@1.1, z@2.x)|  +-- x 1.1.0      <-- x@1.x разрешается в 1.1.0|  +-- z 2.0.0 (x@1.x)  <-- здесь зависимость x@1.x+-- z 1.0.0

Пакет z@2.0.0 зависит от x@1.x, то же самое можно сказать и о root. Файл yarn.lock сопоставляет x@1.x c 1.2.0. Однако зависимость пакета z, где тоже указано x@1.x, вместо этого, будет разрешена в x@1.1.0.

В результате, даже хотя зависимость x@1.x описана в yarn.lock, где указано, что она должна разрешаться в версию пакета 1.2.0, имеется второй результат разрешения x@1.x в пакет версии 1.1.0.

Если запустить npm с флагом --prefer-dedupe, то система пойдёт на шаг дальше и установит лишь один экземпляр зависимости x, что приведёт к формированию следующего дерева зависимостей:

root (x@1.x, y@1.x, z@1.x)+-- x 1.1.0    <-- x@1.x для всех зависимостей разрешается в версию 1.1.0+-- y (x@1.1, z@2.x)|  +-- z 2.0.0 (x@1.x)+-- z 1.0.0

Это минимизирует дублирование зависимостей, получившееся дерево зависимостей фиксируется в файле package-lock.json.

Так как файл yarn.lock фиксирует лишь порядок разрешения зависимостей, а не результирующее дерево пакетов, Yarn сформирует такое дерево зависимостей:

root (x@1.x, y@1.x, z@1.x) <-- здесь зависимость x@1.x+-- x 1.2.0        <-- x@1.x разрешается в 1.2.0+-- y (x@1.1, z@2.x)|  +-- x 1.1.0      <-- x@1.x разрешается в 1.1.0|  +-- z 2.0.0 (x@1.x)  <-- x@1.1.0 тут бы подошёл, но...|    +-- x 1.2.0    <-- Yarn создаёт дубликат ради выполнения того, что описано в yarn.lock+-- z 1.0.0

Пакет x, при использовании Yarn, появляется в дереве зависимостей три раза. При применении npm без дополнительных настроек 2 раза. А при использовании флага --prefer-dedupe лишь один раз (хотя тогда в дереве зависимостей оказывается не самая новая и не самая лучшая версия пакета).

Все три получившихся дерева зависимостей можно признать корректными в том смысле, что каждый пакет получит те версии зависимостей, которые соответствуют заявленным требованиям. Но нам не хотелось бы создавать деревья пакетов, в которых слишком много дубликатов. Подумайте о том, что будет, если x это большой пакет, у которого есть много собственных зависимостей!

В результате имеется единственный способ, используя который, npm может оптимизировать дерево пакетов, поддерживая, в то же время, создание детерминированных и воспроизводимых деревьев зависимостей. Этот способ заключается в применении lock-файла, принцип формирования и использования которого на фундаментальном уровне отличается от yarn.lock.

Фиксация результатов реализации намерений пользователя


Как уже было сказано, в npm 7 пользователь может использовать флаг --prefer-dedupe для того чтобы был бы применён алгоритм генерирования дерева зависимостей, при выполнении которого приоритет отдаётся дедупликации зависимостей, а не стремлению всегда устанавливать самые свежие версии пакетов. Применение флага --prefer-dedupe обычно идеально подходит в ситуациях, когда дублирование пакетов нужно свести к минимуму.

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

root (x@1.x, y@1.x, z@1.x) <-- здесь зависимость x@1.x+-- x 1.1.0        <-- x@1.x разрешается в 1.1.0 во всех случаях+-- y (x@1.1, z@2.x)|  +-- z 2.0.0 (x@1.x)  <-- здесь зависимость x@1.x+-- z 1.0.0

В данном случае npm видит, что даже хотя x@1.2.0 это самая свежая версия пакета, удовлетворяющая требованию x@1.x, вместо неё вполне можно выбрать x@1.1.0. Выбор этой версии приведёт к меньшему уровню дублирования пакетов в дереве зависимостей.

Если не фиксировать структуру дерева зависимостей в lock-файле, то каждому программисту, работающему над проектом в команде, пришлось бы настраивать свою рабочую среду точно так же, как её настраивают остальные члены команды. Только это позволит ему получить тот же результат, что и остальные. Если реализация механизма построения дерева зависимостей может быть изменена подобным способом, это даёт пользователям npm серьёзные возможности по оптимизации зависимостей в расчёте на собственные специфические нужды. Но, если результаты создания дерева зависят от реализации системы, это делает невозможным создание детерминированных деревьев зависимостей. Именно к этому приводит использование файла yarn.lock.

Вот ещё несколько примеров того, как дополнительные настройки npm способны приводить к созданию отличающихся друг от друга деревьев зависимостей:

  • --legacy-peer-deps, флаг, который заставляет npm полностью игнорировать peerDependencies.
  • --legacy-bundling, флаг, говорящий npm о том, что он не должен даже пытаться сделать дерево зависимостей более плоским.
  • --global-style, флаг, благодаря которому всех транзитивные зависимости устанавливаются в виде вложенных зависимостей, в папках зависимостей более высокого уровня.

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

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

Производительность и полнота данных


Файл package-lock.json приносит пользу не только тогда, когда нужно обеспечить детерминированность и воспроизводимость деревьев зависимостей. Мы, кроме того, полагаемся на этот файл для отслеживания и хранения метаданных пакетов, значительно экономя время, которое иначе, с использованием только package.json, ушло бы на работу с реестром npm. Так как возможности файла yarn.lock сильно ограничены, в нём нет метаданных, которые нам нужно постоянно загружать.

В npm 7 файл package-lock.json содержит всё, что нужно npm для полного построения дерева зависимостей проекта. В npm 6 эти данные хранятся не так удобно, поэтому, когда мы сталкиваемся со старым lock-файлом, нам приходится нагружать систему дополнительной работой, но это делается, для одного проекта, лишь один раз.

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

Будущие возможности


То, о чём мы тут говорили, может серьёзно измениться, если учитывать различные новые подходы к размещению зависимостей на дисках. Это pnpm, yarn 2/berry и PnP Yarn.

Мы, работая над npm 8, собираемся исследовать подход к формированию деревьев зависимостей, основанный на виртуальной файловой системе. Эта идея смоделирована в Tink, работоспособность концепции подтверждена в 2019 году. Мы, кроме того, обсуждаем идею перехода на что-то вроде структуры, используемой pnpm, хотя это, в некотором смысле, даже более масштабное кардинальное изменение, чем использование виртуальной файловой системы.

Если все зависимости находятся в некоем центральном хранилище, а вложенные зависимости представлены лишь символьными ссылками или виртуальной файловой системой, тогда моделирование структуры дерева зависимостей было бы для нас не таким важным вопросом. Но нам всё ещё нужно больше метаданных, чем способен предоставить файл yarn.lock. В результате больше смысла имеет обновление и рационализация существующего формата файла package-lock.json, а не полный переход на yarn.lock.

Это не статья, которую можно было бы назвать О вреде yarn.lock


Мне хотелось бы особо отметить то, что, судя по тому, что я знаю, Yarn надёжно создаёт корректные деревья зависимостей проектов. И, для определённой версии Yarn (на момент написания материала это относится ко всем свежим версиям Yarn), эти деревья являются, как и при использовании npm, полностью детерминированными.

Файла yarn.lock достаточно для создания детерминированных деревьев зависимостей с использованием одной и той же версии Yarn. Но мы не можем полагаться на механизмы, зависящие от реализации менеджера пакетов, учитывая использование подобных механизмов во многих инструментах. Это ещё более справедливо, если учесть то, что реализация формата файла yarn.lock нигде формально не документирована. (Это не проблема, уникальная для Yarn, в npm сложилась такая же ситуация. Документирование форматов файлов это довольно серьёзная работа.)

Лучший способ обеспечения надёжности построения строго детерминированных деревьев зависимостей, это, в долгосрочной перспективе, фиксация результатов разрешения зависимостей. При этом не стоит полагаться на веру в то, что будущие реализации менеджера пакетов будут, при разрешении зависимостей, идти тем же путём, что и предыдущие реализации. Подобный подход ограничивает наши возможности по конструированию оптимизированных деревьев зависимостей.

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

Только package-lock.json, или механизм, подобный этому файлу, способен дать npm такие возможности.

Каким менеджером пакетов вы пользуетесь в своих JavaScript-проектах?

Подробнее..

Как npm обеспечивает безопасность

20.08.2020 12:11:50 | Автор: admin

Как npm обеспечивает безопасность


Всем привет! В предыдущих постах мы подробно поговорили про выбор зависимостей и использование lock-файлов в npm, однако я только вскользь касался вопросов безопасности. Пришло время исправить этот недочет: этот и следующий посты будут полностью посвящены безопасности в npm! И для начала мы рассмотрим, как безопасность обеспечивается на уровне инфраструктуры npm и экосистемы в целом.


Мы живем в опасное время


В наше сложное время, когда частные данные утекают рекой, а крупные скандалы со взломами происходят чуть ли не каждую неделю, вопрос безопасности становится как никогда актуальным. С каждым годом технологии усложняются, количество различных API и библиотек растет безумными темпами, а следовательно, увеличивается и поверхность для возможных хакерских атак. Несмотря на всё это, разработчики очень редко действительно задумываются о безопасности и откладывают её в самый дальний угол (сразу за исправлением багов, написанием тестов и рефакторингом архитектуры). То есть до безопасности руки у разработчиков часто доходят только тогда, когда случается какой-то реальный взлом или его попытка, уже после того, как компания понесла финансовые и, что более важно, репутационные потери.


Я предлагаю не дожидаться того момента, когда ваша компания пополнит печальный список взломанных ресурсов, и принять меры для обеспечения безопасности прямо сейчас. Ведь тот, кто предупрежден вооружен.


npm как источник угроз


Опытные хакеры используют множество различных векторов атак, начиная с уязвимостей в железе или операционных системах, и заканчивая уязвимостями серверного ПО и самих приложений. Как ни печально, но npm и загружаемый из него код библиотек тоже являются источником угроз, который нельзя сбрасывать со счетов.


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



Малоизвестно, но разработчики npm активно использовали помощь компании ^Lyft Security (эксперта по компьютерной безопасности) с самого начала разработки платформы: специалисты из Lyft не только активно проверяли исходный код npm на наличие уязвимостей, но и проводили регулярные аудиты безопасности и pen-тесты серверной инфраструктуры. Помимо прочего, консультанты из Lyft активно участвовали в разработке так называемой Node Security Platform (NSP) [платформы безопасности Node], которая отвечает за безопасность экосистемы Node и npm-пакетов. Партнерство двух компаний оказалось настолько удачным, что в начале 2018-го года npm Inc. просто купила ^Lyft Security, и её эксперты по безопасности напрямую влились в команду npm.


Таким образом, компания npm обладает выделенной командой экспертов по безопасности, которые занимаются исключительно этими вопросами, существенно улучшая здоровье самой крупной в мире экосистемы (JavaScript). За всё время работы над Node Security Platform компании npm удалось внедрить много важных решений и инструментов, которые значительно повышают безопасность и позволяют нам чуточку лучше спать по ночам.


Кроме того, в начале 2020 года компания GitHub (Microsoft) купила npm Inc., что автоматически увеличило ресурсы компании и открыло прямой путь для интеграции между npm и GitHub. А, как известно, у GitHub также имеется своя серьезная команда по безопасности GitHub Security Lab. Наличие таких значительных ресурсов и возможностей для коллаборации должно еще сильнее повысить безопасность экосистемы, ведь теперь Microsoft может полностью отследить и обезопасить путь от исходного кода на GitHub до скомпилированного пакета в npm registry.




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


Сканирование пакетов


Платформа npm автоматически анализирует все пакеты в момент их публикации на наличие потенциальных рисков. Система безопасности не только анализирует код каждого пакета статически, но и применяет методы поведенческого анализа для выявления различных угроз. При этом проверяются скрипты установки, сетевая активность и другие факторы на предмет подозрительного поведения. Также система использует дельта-анализ, сравнивая новую версию пакета с предыдущей для выявления конкретных изменений и их дальнейшего анализа. К факторам, которые влияют на оценку безопасности, относится и то, был ли загружен пакет с использованием двухфакторной аутентификации, или был ли он загружен из сети TOR (которой часто пользуются злоумышленники).


В момент сканирования пакета система сверяется, в том числе, и с базами данных СПАМа для выявления нежелательных пакетов, а также проверяет названия новых пакетов на сходство с названиями уже имеющихся: злоумышленники в прошлом подменяли пакеты, пользуясь невнимательностью пользователей, которые совершали опечатки в названии пакета в момент его установки.


Чтобы повысить эффективность сканера, команде npm удалось собрать самую большую в мире библиотеку вредоносного кода на JavaScript, которая постоянно пополняется. Эта библиотека, в том числе, содержит списки опасных доменных имен, IP-адресов и URL, которые могут использоваться злоумышленниками, а также другие индикаторы заражения. Команда npm планирует открыть доступ к этой библиотеке, которая получила название Security Insight API. Это позволит энтузиастам со стороны разрабатывать собственные решения, которые еще более повысят безопасность экосистемы.


Автоматический отзыв токенов аутентификации


npm поддерживает аутентификацию при помощи специальных токенов вместо логина и пароля. Это необходимо для публикации пакетов в npm registry, для работы с закрытыми (private) пакетами в рамках автоматизированных процессов (например, используя CI/CD-конвейер), а также для интеграции со сторонними решениями.


Однако возникали ситуации, когда разработчики по ошибке выкладывали свои секретные токены в открытый доступ, например, в содержимом npm-пакета или в исходном коде на GitHub. Такая утечка очень существенна, потому что токен может давать полные права на управление аккаунтом npm, а значит, злоумышленник сможет от лица автора опубликовать вредоносную версию пакета или удалить пакеты автора из репозитория.



Чтобы нивелировать эту угрозу, npm совместно с GitHub проверяют все публикуемые в открытом доступе материалы на наличие строк, похожих на токены аутентификации; затем сканер обращается к серверу npm и проверяет, является ли найденный токен действительно рабочим токеном npm, и если это так, то npm автоматически блокирует такой токен и отправляет уведомление об этом разработчику на почту. Таким образом, даже если ваш токен случайно окажется в публичном доступе, то он автоматически будет отозван, и злоумышленник не сможет им воспользоваться.


Однако нужно учитывать, что если npm автоматически отзовет токен, который необходим для работы какой-то критической инфраструктуры (например, конвейера по выкатке приложения в production), то это приведет к поломке, потому что интеграция с npm registry будет нарушена.


Компрометация паролей



Довольно часто пользователи используют один и тот же пароль между различными сервисами и аккаунтами. При этом они могут даже не догадываться, что их данные оказались в публичном доступе из-за утечки с одного из сайтов, где они были зарегистрированы. Сервис Have I Been Pwned содержит огромную базу данных утечек и позволяет по вашему E-mail адресу определить, попали ли именно ваши данные в одну из них. Например, мой адрес на GMail, которым я пользуюсь уже 11 лет, попал аж в 14 утечек. Я уверен, вас тоже ждут интересные новости!


А теперь о хорошем: каждый раз, когда вы вводите ваш пароль при аутентификации в npm, а также при его назначении и замене, npm сверяет пароль с базой данных скомпрометированных паролей (разумеется, безопасным способом), и в случае обнаружения запрещает его использование. Таким образом, если ваш пароль попадет в утечку и злоумышленник решит им воспользоваться, у него ничего не выйдет (конечно, при условии, что npm успеет обновить базу данных утекших паролей до этого момента).


Ручной аудит пакетов


Одним из важнейших способов обнаружения уязвимостей является ручной аудит пакетов. Несмотря на то, что команда безопасности npm работает круглосуточно в режиме 24/7, проверить все публикуемые пакеты в ручном режиме невозможно, но это и не требуется. Сообщество разработчиков JavaScript, а также различные партнеры и третьи стороны каждый день отправляют в npm отчеты о найденных уязвимостях с указанием проблемных пакетов и их версий. В неделю npm насчитывает порядка 25 подобных обращений. Специалисты из команды безопасности тщательно проверяют каждый отчет об уязвимости и стараются подтвердить проблему в лабораторных условиях на специальном исследовательском стенде.


В случае обнаружения вредоносного кода (malware), проблемный пакет удаляется из реестра npm, чтобы предотвратить его дальнейшее распространение. Кроме того, команда npm пытается вычислить и другие пакеты, которые могут содержать аналогичный вредоносный код или иметь схожее вредоносное поведение.


При обнаружении уязвимости специалисты по безопасности незамедлительно связываются с владельцем пакета, чтобы проинформировать его об этом и выработать план действий по ликвидации проблемы и предотвращению тяжелых последствий. Команде npm важно, чтобы автор пакета в точности понимал, в чём заключается уязвимость и какие именно изменения необходимо внести в код, чтобы устранить её.


Ещё нужно понимать, что информация об уязвимости обязательно включается в базу данных npm в качестве так называемой security advisory. А учитывая очень широкий охват аудитории (через npm audit), информация становится доступна всем, включая злоумышленников, которые могут воспользоваться найденной уязвимостью в корыстных целях. По этой причине важно, чтобы автор уязвимого пакета узнал об этом первым и мог оперативно принять необходимые меры (выпустить исправленную версию). Если уязвимость уже публично известна, то команда npm дает автору пакета 48 часов на её устранение, после этого уязвимость будет добавлена в общую базу. Если же информация об уязвимости была передана в npm в частном порядке и не публиковалась в открытом доступе, то команда безопасности дает автору пакета 45 дней на выпуск обновления и не публикует информацию официально, чтобы она не попала в руки хакеров, которые непременно захотят ей воспользоваться.



По словам специалистов из npm, из всех отчетов об уязвимостях, которые им присылают, только 20 % в итоге подтверждаются и доходят до публикации. На момент написания этого поста в базе npm было 1427 рекомендаций по безопасности (security advisories). Полный список рекомендаций можно посмотреть на официальном сайте в специальном разделе. Также доступен список рекомендаций со стороны GitHub.


К счастью, чтобы пользоваться базой данных рекомендаций, вам не нужно читать их все, или даже заходить на эти страницы: npm CLI автоматически обращается к этой базе при установке зависимостей и сообщает вам об уязвимостях в пакетах, которые используются в вашем проекте. Подробнее об этом я расскажу в следующем посте.


Продолжение следует


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


npm предоставляет много полезных инструментов, которые требуют внедрения с нашей стороны. Сюда относится двухфакторная аутентификация/авторизация, механизмы ограничения возможностей токенов аутентификации и команда npm audit. Обо всём этом и о том, какие дополнительные меры вы можете применить, мы подробнее поговорим в следующем посте.


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


Если у вас есть вопросы или желание что-то добавить по теме, то не бойтесь оставлять комментарии, я с радостью приму участие в обсуждении и учту ваши пожелания в следующих постах.

Подробнее..

Установка и обновление зависимостей в JavaScript

14.07.2020 12:05:49 | Автор: admin

Установка и обновление зависимостей JavaScript


И снова привет! В прошлом посте мы начали рассматривать процесс управления зависимостями в JavaScript, разобрали основы: что такое npm-пакет; как выглядит манифест пакета; в каких полях прописываются зависимости; что такое дерево зависимостей; а также основы семантического версионирования (semver). Если вы пропустили предыдущий пост, то рекомендую начать с него.


Сегодня мы пойдем немного дальше и подробнее рассмотрим, как работает semver, как правильно прописывать диапазоны зависимостей, а также устанавливать и обновлять их.


npm shell autocomplete


В моих постах я буду часто упоминать npm и различные команды с его использованием. Чтобы сделать набор команд в терминале чуточку удобнее, предлагаю установить автодополнения в ваш shell.


Сделать это достаточно легко, достаточно выполнить следующие команды:


  • Для Bash:


    npm completion >> ~/.bashrcsource ~/.bashrc
    

  • Для Z shell:


    npm completion >> ~/.zshrcsource ~/.zshrc
    

    Это добавит в конфигурационный файл shell-а необходимый скрипт. После этого, если вы напишите npm smth, а затем нажмете [TAB], shell автоматически дополнит вашу команду или предложит варианты дополнения.



Инициализация проекта


Как мы уже успели обсудить, проектом в npm является любая директория, в которой находится манифест (файл package.json). Вы можете создать манифест вручную в любом редакторе кода, либо выполнить команду npm init. Она по умолчанию задаст вам серию вопросов в интерактивном режиме и сгенерирует простейший манифест в текущей директории на основе ваших ответов.


Однако я предпочитаю вызывать команду следующим образом: npm init -y, а затем править манифест в редакторе. При таком вызове npm не будет задавать вопросов, а просто сгенерирует минимальный манифест со значениями по умолчанию.


Использование инициализаторов


Говоря про npm init нельзя не упомянуть про возможность использования специальных пакетов инициализаторов (npm initializers). Они облегчают создание новых проектов, генерируя необходимый boilerplate-код.


Используются пакеты так: npm init <initializer>, где <initializer> это название инициализатора (например, esm или react-app).


Инициализатор, по сути, это специальный npm-пакет с префиксом create-, который загружается из npm registry в момент вызова команды и выполняется. У каждого инициализатора могут быть свои аргументы, настройки и поведение.


Например, можно создать React-приложение с помощью инициализатора `create-react-app: npm init react-app -- my-react-app. Два минуса позволяют разделить аргументы CLI, которые передаются в команду npm init от тех, что передаются в сам инициализатор. Особенность инициализатора React, например, в том, что проект будет создан не в текущей директории, а в поддиректории с названием, которое вы укажете при вызове (в примере: my-react-app).


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


Добавление зависимостей в проект


Как мы выяснили ранее, зависимости прописываются в манифесте проекта в полях dependencies, devDependencies, peerDependencies или optionalDependencies. Чтобы добавить новую зависимость в проект, необходимо использовать команду: npm install <package-name>, или сокращенно: npm i <package-name>.


Пример: npm i lodash.


Эта команда установит lodash самой свежей стабильной версии и добавит эту зависимость в поле dependencies манифеста проекта.


Аналогично можно устанавливать сразу несколько зависимостей одновременно: npm i lodash express passport.


Команда install с флагами также позволяет выбрать, в какое поле будет добавлена зависимость:


  • -P, --save-prod
    установка в dependencies (работает по умолчанию)
  • -D, --save-dev
    установка в devDependencies
  • -O, --save-optional
    установка в optionalDependencies

Если вам нужно установить зависимость в peerDependencies, то придётся сделать это вручную т. к. npm не предусматривает для этого специальной команды. Как вариант, можно сначала установить зависимость в dependencies при помощи команды npm install, а потом перенести ее вручную в peerDependencies. В этом случае вам не придется угадывать свежую версию пакета (если вдруг ваш IDE не поддерживает автоматическую интеграцию с npm).


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


Команда npm install является многоцелевой командой, т. к. одновременно решает сразу несколько задач: установку уже прописанных зависимостей в проект и добавление новых зависимостей с их прописыванием в манифесте.

Добавление зависимости старой версии


Если по какой-то причине вы хотите добавить зависимость не самой свежей версии, то вы можете указать нужную версию через символ @:


  • npm i lodash@3.9.2
  • npm i lodash@3

Однако делать это рекомендуется только в самом крайнем случае. Об этом я расскажу подробнее чуть позже.


Установка зависимостей


Выше мы рассмотрели варианты добавления зависимостей в проект. Но как установить зависимости, которые уже прописаны в манифесте, если вы, к примеру, только сделали git clone?


Для этого достаточно просто выполнить команду npm install или npm i без аргументов. Npm прочитает содержимое манифеста, найдет указанные зависимости и установит их в проект.


Существует возможность установить только одну категорию зависимостей:


  • --only=prod[uction]
  • --only=dev[elopment]

Это может быть полезно, если вы хотите, к примеру, только запустить программу на Node.js, но не работать над ней.


Просмотр установленных зависимостей


Прежде чем мы поговорим про обновление зависимостей, было бы полезно научиться просматривать их. Для этой цели в npm также предусмотрена специальная команда: npm ls.


Синтаксис команды выглядит следующим образом: npm ls [<package-name>], где <package-name> опциональное название пакета, который вы хотите найти в дереве зависимостей.


Если вызвать команду без аргументов, то она выведет полное дерево зависимостей (не ослепните, дерево может быть огромным):



Крошечная порция результата выдачи команды npm ls в большом проекте.


Иногда бывает необходимо найти ту или иную транзитивную зависимость в дереве проекта, чтобы понять, какие пакеты тянут ее, для этого можно передать название пакета в команду: npm ls lodash.



Результат поиска пакета lodash в дереве зависимостей крупного проекта при помощи команды npm ls lodash.


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


npm ls --depth=0

Как вы уже догадались, по умолчанию команда работает со всем деревом целиком.


Также вы можете использовать опции dev или prod для того, чтобы вывести только зависимости из полей dependencies или devDependencies:


  • npm ls --dev[elopment]
  • npm ls --prod[uction]

Использование же опции json позволяет получить дерево зависимостей в формате. пригодном для машинной обработки. В этом режиме npm выводит более подробную информацию о пакетах, а не только название и версию. Режим может быть также полезен для отладки и сравнения двух деревьев (например, в разных ветках проекта):


npm ls --json

Обновление зависимостей


Как мы уже рассмотрели в предыдущем посте, в npm для управления зависимостями используется система семантического версионирования (semver). Благодаря ей вы можете обновлять зависимости в своем проекте с предсказуемыми результатами: к примеру, если зависимость обновилась с версии 1.2.3 до версии 1.2.4 (patch update) или 1.3.0 (minor update), то это не сломает ваш проект, т. к. по правилам semver такие обновления не должны нарушать обратной совместимости. А если обновление производится с версии 1.2.3 до версии 2.0.0 или выше, то здесь вам следует обязательно заглянуть в журнал изменений (changelog) данного пакета, чтобы убедиться, что обновление ничего не сломает. Возможно, вам придется внести изменения в свой код, чтобы восстановить совместимость.


Несмотря на то, что semver гарантирует достаточно высокий уровень безопасности при обновлении, бывают случаи, когда разработчики какого-то пакета могут нарушать правила и вносить критические изменения в patch- или minor-обновлениях (в первую очередь это касается непопулярных пакетов, которыми управляют менее опытные разработчики).

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

Версии зависимостей


Давайте теперь рассмотрим, как именно прописываются версии зависимостей в манифесте проекта и какие механизмы дает нам semver для управления процессом обновления. Как я уже упомянул выше, при установке зависимости npm автоматически устанавливает самую свежую версию и включает наиболее свободный режим обновления для этой зависимости: разрешает как patch-, так и minor-обновления.


В package.json это выглядит следующим образом:


{  "dependencies": {    "lodash": "^4.17.17"  }}

Символ ^ (caret, hat или крышечка) указывается перед номером версии и имеет особое значение в semver. В данном случае это означает, что версия зависимости lodash должна обновляться до максимально доступной, но не выше 5.0.0, т. е. разрешает patch- и minor-обновления, но запрещает обновления, нарушающие обратную совместимость.


Помимо крышечки существует еще довольно много способов задать диапазон версии, все их можно посмотреть в описании пакета semver, который и реализует эту функциональность в npm. Однако в 99 % случаев зависимости стоит указывать именно через крышечку (как делает npm по умолчанию), т. к. это дает наибольшую гибкость при обновлении зависимостей, и в то же время защищает вас от критических изменений.


Фиксация версий зависимостей


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


{  "dependencies": {    "lodash": "4.17.17"  }}

Это гарантирует, что lodash будет установлен именно версии 4.17.17, ни больше, ни меньше.


Однако фиксация версий зависимостей имеет ряд существенных недостатков:


  • такие зависимости перестают обновляться, вы перестанете автоматически получать исправления багов, а самое главное улучшения безопасности,
  • если в дереве зависимостей вашего проекта этот пакет встречается более одного раза, то это приведет к тому, что он будет установлен несколько раз (несколько разных версий). Про дублирование зависимостей мы подробнее поговорим в будущем, но пока поверьте мне на слово, это может приводить к существенным сложностям.

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


Просмотр устаревших зависимостей


Новые версии пакетов выходят регулярно. Поэтому пакеты, установленные в вашем проекте, могут устаревать и их необходимо регулярно обновлять. Команда npm outdated позволяет вывести список устаревших пакетов.



Результат команды npm outdated в проекте, где установлены две устаревшие зависимости.


Эта команды выводит таблицу со следующими колонками:


Колонка Описание
Package Название пакета
Current Текущая установленная версия
Wanted Максимальная версия, которая удовлетворяет диапазону semver, прописанному в манифесте проекта
Latest Версия пакета, которую автор указал в качестве самой свежей (как правило, максимально доступная версия пакета)
Location Место расположения зависимости в дереве

По умолчанию команда npm outdated выводит список прямых зависимостей вашего пакета. Однако, если использовать аргумент depth с указанием глубины просмотра, то npm покажет устаревшие зависимости, в том числе и на заданной глубине дерева зависимости:


npm outdated --depth=10

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


npm outdated --depth=9999

Обновление устаревших зависимостей


Обновить устаревшие зависимости в npm можно при помощи команды npm update.


Команда проверяет версии установленных зависимостей по отношению к версиям, доступным в npm registry, с учётом диапазонов версий semver, указанных в манифесте вашего проекта. Если установленная версия того или иного пакета отличается от максимальной версии, доступной в registry (учитывая ограничение semver), то более свежая версия будет загружена и установлена, а манифест будет обновлен, чтобы минимальная версия в диапазоне соответствовала установленной. Важно заметить, что весь этот процесс протекает без нарушения semver, т. е. вызов npm update никогда не приведет к нарушению диапазонов версий, указанных в вашем манифесте.


Приведем пример: допустим, в вашем проекте указан диапазон версий пакета lodash: ^4.16.4. Вызов npm update приведет к тому, что пакет будет обновлен до версии 4.17.19, а манифест будет автоматически изменен, чтобы содержать диапазон ^4.17.19.


По аналогии с командой npm outdated, команда npm update также поддерживает аргумент depth и по умолчанию обновляет только прямые зависимости проекта, не трогая зависимости в глубине дерева. Поэтому, чтобы обновить все зависимости в проекте необходимо вызывать команду следующим образом:


npm update --depth=9999

Это гарантирует. что абсолютно все зависимости в дереве (включая транзитивные) будут обновлены до самых последних версий. Такую процедуру рекомендуется делать регулярно, чтобы в вашем проекте всегда был самый свежий код с последними исправлениями багов и проблем безопасности.


npm-check


В качестве альтернативы командам npm outdated и npm update хочу предложить интересный инструмент под названием npm-check.


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


npm i -g npm-check

Инструмент поддерживает интерактивный режим, позволяя вам выбрать галочками те пакеты, которые вы хотите обновить. Кроме того, он позволяет обновлять пакеты не только в рамках ограничений semver, но и игнорируя их, перепрыгивая с одной мажорной версии зависимости на другую. Разумеется, делать это нужно осмысленно: тщательно изучая журнал изменений каждого мажорно-обновляемого пакета на наличие нарушений обратной совместимости. И никогда не забывайте тестировать ваш код после любых обновлений!



Результат вызова npm-check в проекте: доступно два обновления, одно мажорное и одно минорное.



Также результат вызова npm-check, но уже в интерактивном режиме: галочками можно выбрать зависимости, которые вы хотите обновить.


В качестве очень полезного бонуса: npm-check позволяет обнаружить, если какая-то из зависимостей не используется в проекте:



npm-check сообщает о том, что пакет lodash, возможно, не используется в проекте.


Рекомендую всегда держать этот незаменимый инструмент (или аналогичный) в своем арсенале.


Удаление зависимостей


Ну и наконец давайте рассмотрим, как мы можем удалять ранее добавленные зависимости в проект. Для этого существует команда npm uninstall, или сокращенно rm, r или un. Чтобы удалить один или несколько проектов, мы можем вызвать команду следующим образом:


npm rm lodash express

Будут удалены указанные пакеты как из файловой системы проекта, так и из его манифеста.


Workflow работы с npm-проектом


Мы уже рассмотрели несколько полезных команд и инструментов, давайте попробуем обобщить наши знания для того, чтобы выработать некий workflow для работы над JavaScript-проектами. Не переживайте, потом мы его доработаем, когда углубимся в другие важные вопросы.


  • Инициализация проекта:
    • npm init
      (интерактивно)
    • npm init -y
      (с последующим редактированием в IDE)
  • Добавление зависимостей:
    • npm install <dependency>
    • npm install <dependency-1> <dependency-2>
    • npm install -D <dev-dependency>
  • Проверка и обновление зависимостей:
    • npm outdated
      (просмотр прямых устаревших зависимостей)
    • npm outdated --depth=9999
      (просмотр всех устаревших зависимостей)
    • npm update
      (обновление прямых устаревших зависимостей с учетом semver)
    • npm update --depth=9999
      (обновление всех устаревших зависимостей с учетом semver)
    • npm-check
      (просмотр прямых устаревших зависимостей)
    • npm-check -u
      (интерактивное обновление прямых устаревших зависимостей)
  • Удаление зависимостей:
    • npm rm <dependency>

Продолжение следует


Мы подробнее рассмотрели процесс инициализации проекта, добавления, установки и обновления зависимостей. Рассмотрели, как semver работает на практике при обновлении зависимостей.


В следующей статье мы детально поговорим про такую важную концепцию, как lock-файлы npm (shrinkwrap), и научимся пользоваться этими механизмами для повышения стабильности при работе с зависимостями. Увидимся через неделю, а пока подписывайтесь и следите за обновлениями!

Подробнее..

Lock-файлы npm

30.07.2020 14:08:36 | Автор: admin

Lock-файлы npm


Всем привет! В прошлом посте мы рассмотрели экосистему npm в качестве источника хаоса в нашем проекте, и научились с умом подходить к выбору зависимостей, чтобы минимизировать наши риски. Сегодня мы пойдем дальше и рассмотрим lock-файлы npm, которые помогают повысить стабильность проекта в процессе работы над ним.


Когда манифеста мало


Как мы уже определили, npm берёт на входе манифест проекта (файл package.json) и описанные в нем зависимости, а на выходе мы получаем локально сгенерированную директорию node_modules, в которой содержатся все установленные зависимости.


Не будем сейчас касаться деталей структуры node_modules, однако замечу, что установленные пакеты не всегда имеют плоскую структуру, а могут в отдельных случаях формировать некую иерархию, когда один или несколько пакетов вложены в другой пакет. Как правило, это происходит, когда в дереве зависимостей присутствует один и тот же пакет, но разных версий. При этом сама структура в файловой системе не детерминирована на 100 %, и может зависеть, в том числе, от порядка установки зависимостей.


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


Вы помните, что список зависимостей в манифесте проекта содержит диапазон версий semver, что позволяет обновлять зависимости? Получается, что установка зависимостей в разное время будет приводить к разным результатам, потому что содержимое npm registry постоянно меняется, регулярно выходят новые пакеты. Кроме того, поскольку мы имеем дело с деревом зависимостей, то транзитивные зависимости (зависимости ваших зависимостей) тоже будут меняться во времени.


А еще может возникнуть ситуация, когда содержимое пакета, лежащего в npm registry, было заменено без обновления версии. Это называется мутацией пакетов и запрещено в официальном npm registry, однако в частных реестрах вполне возможно. А что, если содержимое пакета было заменено злоумышленником, чтобы внести в код какие-то закладки?


Учитывая все эти факторы, становится очевидно, что структура данных в директории node_modules очень нестабильна и может меняться во времени, даже если ваш манифест при этом остается нетронутым.


Наивно было бы попытаться зафиксировать зависимости, прописывая строгие версии в манифесте проекта (вместо диапазонов semver): как мы рассмотрели выше, это не даст существенных результатов, потому что транзитивные зависимости всё равно будут обновляться. Да и другие факторы не перестанут влиять на установку зависимостей. Кроме того, если вы заморозите ваши прямые зависимости, то получится ситуация, когда более старые версии будут работать с более свежими версиями транзитивных зависимостей, и потенциально это повышает вероятность проблем с интеграцией.


А теперь представьте, что у нас в проекте есть конвейер CI/CD и специальный сервер, который собирает, тестирует и выкатывает приложения в разные среды выполнения. Как правило, такие решения привязываются к ID коммита в Git (или к Git-тегам), и на каждый коммит система генерирует готовый к выкатке артефакт (архив с готовыми для выполнения файлами). Таким образом, на вход конвейера поступает код из Git-репозитория, версионированный через ID коммита, а на выходе вы получаете протестированный и готовый к выкатке артефакт. В идеале, это должно работать как чистая функция (pure function): если вы пересоберёте коммит, созданный несколько месяцев назад, то должны получить на выходе тот же самый артефакт. Однако мы не можем хранить содержимое node_modules в Git, и получается, что после клонирования репозитория нам необходимо вызывать установку зависимостей из реестра npm. А, как мы уже выяснили, этот процесс довольно нестабилен и привязан к глобальному состоянию экосистемы (содержимому npm registry, версиям npm и т. д.). Получается, что npm вносит хаос в наш конвейер CI/CD и мы уже не можем получить одинаковую сборку по ID коммита.


Lock-файлы приходят на помощь


Чтобы предотвратить все описанные выше проблемы и сделать использование зависимостей гораздо более стабильным, npm (как и любой другой современный менеджер) предлагает специальный механизм заморозки зависимостей. Работает это автоматически и прямо из коробки: впервые вызывая команду npm install, npm не только устанавливает все зависимости и создаёт директорию node_modules, он также создает специальный файл package-lock.json. Этот файл называется lock-файлом и содержит в себе полную информацию обо всех установленных зависимостях, включая их точные версии, URL npm registry, из которого был скачан пакет, а также SHA-хэш самого архива с пакетом. Помимо прочего, lock-файл npm описывает еще и порядок установки зависимостей, и их вложенность в файловой системе.


При повторном вызове команды npm install менеджер пакетов увидит, что lock-файл содержится в директории проекта, и в дальнейшем зависимости будут устанавливаться в полном соответствии с информацией из lock-файла. Таким образом, вы можете вызывать команду npm install сколько угодно раз на любой машине и в любое время (даже спустя месяцы), и на выходе будете получать одинаковую структуру файлов в директории node_modules. Также стоит заметить, что установка зависимостей через lock-файл осуществляется быстрее, потому что npm не нужно сверять диапазоны зависимостей из манифеста с данными, доступными в реестре npm. Даже если версия npm обновится и алгоритм установки зависимостей поменяется, lock-файл всё равно будет гарантировать точный и стабильный результат, потому что файл, по сути, описывает результат работы алгоритма на основе входных данных. В каком-то смысле эта концепция аналогична кэшированию.


Чтобы использовать преимущества lock-файла, его необходимо добавить в систему контроля версий. Таким образом, вы строго привяжете полное дерево зависимостей к коммитам в Git. Это будет гарантировать стабильное воспроизводство сборок в вашей системе CI/CD и позволит надежно путешествовать во времени.


Кроме того, каждый разработчик, который склонирует Git-репозиторий к себе на машину, получит точно такое же дерево зависимостей, как и у вас. Это устранит известную проблему из разряда странно, а у меня всё работает (it works on my machine).


it worked on my machine


Структура package-lock.json


Npm генерирует lock-файл полностью автоматически на основе данных из манифеста проекта, глобального состояния npm registry и алгоритма установки зависимостей npm. Однако содержимое файла вполне читаемо человеком и может быть использовано даже на этапе code review. Diff lock-файла покажет, какие зависимости в дереве были обновлены, какие были удалены, а какие добавлены. Наверное, нет смысла изучать изменения этого файла при каждом обновлении, но при обнаружении каких-то деградаций это может сильно помочь в поиске виновного пакета и сэкономить вам кучу времени. Но чтобы это работал эффективнее и размер изменений был минимальным, я рекомендую обновлять зависимости как можно чаще (гораздо проще выявить проблему, если у вас обновилось три пакета в дереве зависимостей, а не сотня).


Давайте теперь рассмотрим содержимое файла package-lock.json в тестовом проекте, где установлена только одна зависимость express.


Оригинальный файл содержит почти 400 строк, но чтобы пример получился достаточно читабельным и наглядным, я показал только часть дерева зависимостей.

package-lock.json


{  "name": "test",  "version": "1.0.0",  "lockfileVersion": 1,  "requires": true,  "dependencies": {    "express": {      "version": "4.17.1",      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",      "integrity": "sha512-mHJ9O79RqluphRr7xlEMXTnYt4g==",      "requires": {        "debug": "2.6.9",        "send": "0.17.1"      }    },    "debug": {      "version": "2.6.9",      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",      "integrity": "sha512-bC7ElrdJaJnPbAPeAPVMNcKGsHMA==",      "requires": {        "ms": "2.0.0"      }    },    "ms": {      "version": "2.0.0",      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="    },    "send": {      "version": "0.17.1",      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",      "integrity": "sha512-BsVKsiGcQMFwT8UcNuE3V4fT9sAg==",      "requires": {        "debug": "2.6.9",        "depd": "~1.1.2",        "destroy": "~1.0.4",        "encodeurl": "~1.0.2",        "escape-html": "~1.0.3",        "etag": "~1.8.1",        "fresh": "0.5.2",        "http-errors": "~1.7.2",        "mime": "1.6.0",        "ms": "2.1.1",        "on-finished": "~2.3.0",        "range-parser": "~1.2.1",        "statuses": "~1.5.0"      },      "dependencies": {        "ms": {          "version": "2.1.1",          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",          "integrity": "sha512-tgp+dl5cGk28utYYaD/kOWhYQvyg=="        }      }    }  }}

Итак, давайте разбираться. Начнем с основных корневых свойств:


  • name и version тут всё просто, это название и версия проекта из его манифеста на момент создания lock-файла.
  • lockfileVersion это версия формата, в котором представлен lock-файл. Она нужна для расширяемости, если разработчики npm в будущем придумают какой-то новый формат хранения.
  • dependencies полное плоское дерево зависимостей вашего проекта; объект, в котором ключ это название пакета, а значение дескриптор.

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


  • version точная версия пакета на момент установки.
  • resolved URL пакета в реестре npm, откуда он был скачан.
  • integrity SHA-хэш пакета; проверочная сумма, которая позволяет убедиться, что в пакет не было внесено изменений как в процессе скачивания, так и на стороне хранилища (защита от мутации). Это очень важный элемент безопасности при работе с npm, который гарантирует, что злоумышленник не сможет как-то вмешаться в код пакета. При обнаружении несоответствия вызов npm install будет прерван с ошибкой.
  • requires объект, описывающий транзитивные зависимости (копируется из поля dependencies манифеста стороннего пакета). Ключ является названием пакета, а значение диапазоном версий semver.
  • dependencies аналогично полю dependencies, описанному выше. Позволяет рекурсивно описывать структуру вложенных пакетов, когда в дереве зависимостей содержится один и тот же пакет, но разных версий.
  • dev если true, то ээта зависимость является только зависимостью для разработки (необходимо для раздельной установки зависимостей).

Дублирующиеся зависимости


Обратите внимание, что в примере выше пакет express (наша прямая зависимость) зависит от пакета debug, а тот, в свою очередь, от ms@2.0.0. В то же время, пакет send также зависит от ms, но уже версии 2.1.1. Получается, что в директории node_modules пакет ms должен быть установлен два раза (разных версий), но, в силу сложившихся правил в Node.js, два пакета разных версий не могут быть установлены в корне. По этой причине одна версия устанавливается в корень (ms@2.0.0), а вторая в поддиректорию пакета send (ms@2.1.1). Это решение как раз и отражено в lock-файле. В том числе благодаря этому достигается стабильность директории node_modules.


Подробнее про дублирование зависимостей мы поговорим в будущих статьях.


Ручные правки


Хотя структура lock-файла хорошо читается и довольно понятна, нельзя забывать, что этот файл генерируется автоматически. Не пытайтесь вносить в него правки, их будет невозможно сохранить. После очередного обновления lock-файла они будут потеряны.


Конфликт в lock-файлах npm


Если несколько разработчиков трудятся в одной ветке и используют lock-файлы, то в какой-то момент может возникнуть merge-конфликт в Git. В этом случае достаточно просто устранить конфликты в файле манифеста (если они есть), а потом выполнить npm install: менеджер пакетов автоматически исправит конфликты в lock-файле.


Если вам не хочется править конфликты в lock-файлах вручную, то можете установить специальный merge-драйвер для Git, который умеет работать с npm. Это позволит исправлять конфликты в файле package-lock.json автоматически. Однако, если конфликт возникнет в манифесте, то вам всё равно будет необходимо исправить конфликт вручную, а потом вызвать npm install.


Установить merge-драйвер для npm можно следующим образом:


npx npm-merge-driver install -g

При возникновении конфликта при вызове команд Git в консоли будет выведено:


npm WARN conflict A git conflict was detected in package-lock.json. Attempting to auto-resolve.Auto-merging package-lock.json

Обновление lock-файла


Для работы с lock-файлами не требуется каких-то особых действий со стороны разработчика, npm автоматически обновляет lock-файл, когда в этом есть необходимость. Например, если вы вызываете команду npm install lodash, то помимо того, что npm добавит новую зависимость в манифест и установит её, он автоматически обновит lock-файл. Таким образом, npm всегда следит, чтобы lock-файл был в актуальном состоянии.


Однако если вы вносите изменения в манифест проекта вручную, например, добавляя новую зависимость, то возникает дрифт (рассинхронизация) между манифестом и lock-файлом. Менеджер пакетов достаточно умён, чтобы обнаружить этот дрифт: когда вы вызовете команду npm install, npm проанализирует актуальность lock-файла, и если он устарел, то менеджер пакетов автоматически обновит lock-файл, используя данные из манифеста.


Установка зависимостей в CI/CD


Как я сказал выше, если npm обнаружит отставание lock-файла от манифеста, то это приведет к обновлению lock-файла и установке зависимостей из манифеста. Такое поведение очень удобно при разработке, но совершенно непригодно, и даже опасно в контексте конвейера CI/CD, потому что может привести к неожиданным результатам из-за слетевшей блокировки зависимостей.


Чтобы этого не происходило, разработчики npm добавили специальную команду npm ci. В отличие от npm install, эта команда никогда не обновляет lock-файл. Более того, если в проекте отсутствует или устарел lock-файл, то npm ci вернет код ошибки и прервет выполнение конвейера, гарантируя, что ничего плохого не случится (принцип Fail-fast). Кроме того, npm ci полностью удаляет директорию node_modules перед установкой зависимостей, что гарантирует установку на чистый лист.


По этой причине никогда не следует использовать команду npm install в рамках конвейера CI/CD, обязательно используйте npm ci вместо нее. Идите и проверьте это прямо сейчас! (я подожду).


Разные типы проектов


Давайте теперь поговорим про особенности использования lock-файлов в проектах разных типов. Первое, о чём стоит сказать: файл package-lock.json не публикуется в npm registry. Это означает, что если вы публикуете свой пакет в реестр npm (библиотека), то содержимое вашего lock-файла не будет оказывать влияния на дерево зависимостей при установке вашего пакета в чей-то проект. В этом случае играет роль только содержимое вашего манифеста. Это и хорошо: если бы каждая библиотека замораживала свои зависимости, то дерево зависимостей в конечном проекте было бы ещё больше (куда уж больше?) и содержало огромное количество дублей. Адекватно управлять зависимостями стало бы невозможно.


Shrinkwrap


Однако в npm есть специальная команда [npm shrinkwrap](http://personeltest.ru/aways/docs.npmjs.com/cli/shrinkwrap). Она создает файл npm-shrinkwrap.json в корне проекта, который является тем же lock-файлом, только с другим именем и семантикой. Особенность его в том, что, в отличие от package-lock.json, он таки публикуется в реестр npm и оказывает непосредственное влияние на дерево зависимостей при установке вашего пакета. Фактически, он замораживает дерево зависимостей вашего пакета, даже если тот устанавливается в другой проект.


Как я сказал выше, использовать это решение для библиотек очень вредно, поэтому не стоит этого делать. Однако, оно может быть полезно, если вы разрабатываете программу на Node.js, которая должна выполняться на компьютере пользователя (например, аналог webpack, gulp, create-react-app и т. д.). Если программа устанавливается преимущественно глобально на компьютере пользователя (npm i -g), то использование shrinkwrap-файла гарантирует, что на машине пользователя программа будет иметь те же зависимости, что и на вашей машине. Так что, если у вас есть явные причины опасаться дрифта зависимостей в вашей программе, то вы можете воспользоваться npm shrinkwrap. В остальных случаях я не рекомендовал бы использовать эту команду.


Кстати, файл npm-shrinkwrap.json имеет приоритет над файлом package-lock.json. В проекте достаточно только одного файла.


Тестирование пакета-библиотеки


Важным этапом в работе над проектом библиотеки является его тестирование. Дело в том, что вы полностью не контролируете внешние условия, в которых будет вызываться ваша библиотека. Даже если вы зафиксируете прямые зависимости, вы не сможете зафиксировать транзитивные (разве что будете использовать shrinkwrap, но мы уже выяснили, что это плохая практика).


По этой причине вы не можете гарантировать среду, однако можете протестировать работу библиотеки со свежими версиями всех зависимостей, чтобы убедиться, что в новых и свежих проектах ничего не сломается. Однако, если вы будете использовать lock-файл в вашем проекте, то он будет постоянно оттягивать версии ваших зависимостей назад, не давая вам полноценно тестировать совместимость с самыми свежими версиями всех зависимостей (которые наверняка будут у ваших пользователей). Либо вам придется постоянно вызывать глубокий npm update перед каждым тестом.


Руководствуясь этим, некоторые разработчики советует вообще не использовать lock-файлы для проектов библиотек. Однако, я считаю это слишком радикальным советом. Дело в том, что помимо продуктовых runtime-зависимостей вашего пакета существуют еще и dev-зависимости. Если вы откажетесь от lock-файла, то ваши dev-зависимости выйдут из-под контроля, то есть вы потеряете некий островок стабильности.


Более подходящим решением, на мой взгляд, была бы реорганизация конвейера CI/CD таким образом, чтобы код библиотеки тестировался в проекте без использования lock-файла, путем установки самых свежих доступных зависимостей. Собирать же пакет (если требуется) можно и с участием lock-файла (для этого можно использовать два разных этапа в вашем CI/CD конвейере).


С глаз долой


У многих разработчиков lock-файлы вызывают чувство раздражения, видимо, из-за непонимания их предназначения или особенностей их работы. Такие разработчики норовят добавить package-lock.json в .gitignore или вообще настроить npm, чтобы запретить генерирование lock-файлов. При этом они (часто сами того не понимая) жертвуют стабильностью и безопасностью своего приложения, а также теряют достаточно мощный инструмент отслеживания изменений зависимостей в проекте. Часто эти же люди начинают строго прописывать версии зависимостей в основном манифесте, чтобы как-то компенсировать эту нестабильность, не отдавая себе отчета в том, что это не решает проблему, а только создает иллюзию её решения. Я уже не говорю о том, что они используют инструмент не по назначению, теряя гибкость разработки, которая обеспечивается, в том числе, и механизмами семантического версионирования.


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


Обновляйтесь чаще!


Помните, задача lock-файлов заключается не в том, чтобы навсегда заблокировать все зависимости и больше никогда их не обновлять, а в том, чтобы обеспечить повторяемость их установки и прозрачность их обновления.


Залог высокого качества, поддерживаемости и удобства разработки вашего проекта заключается в частом обновлении зависимостей. Согласитесь, гораздо проще обновлять зависимости маленькими порциями по мере их выхода. Diff lock-файла всегда покажет, что именно обновилось. Если он будет небольшим, то вы легко сможете его прочитать. Если же после обновления возникнет проблема, то, скорее всего, она будет одна, и её будет несложно обнаружить и изолировать.


С другой стороны, если вы не будете полгода обновлять зависимости, то потом сделать это будет очень сложно, потому что крупное обновление очень сложно разделить на маленькие части. Вы получите огромный ворох изменений разной степени тяжести, а в случае возникновения проблемы (а она, скорее всего, придет не одна) выявить точное место будет гораздо сложнее (diff здесь уже будет бесполезен в силу его гигантского размера).


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


Продолжение следует


Мы рассмотрели столь важный инструмент повышения надежности и безопасности управления зависимостями, как lock-файлы npm. Использование этого механизма позволит вам существенно повысить стабильность работы вашего проекта и улучшить качество и удобство разработки.


Также мы немного коснулись структуры директории node_modules и явления дублирования пакетов. В следующей статье я хочу поговорить об этом подробнее. Следите за обновлениями!

Подробнее..

Безопасность npm-проектов, часть 1

01.09.2020 12:04:25 | Автор: admin

Безопасность npm-проектов, часть 1


Всем привет! В прошлом посте мы начали рассматривать важный вопрос о безопасности в npm и поговорили о том, какие меры использует сама компания для выявления и предотвращения угроз. В этот раз я хочу поговорить об инструментах и подходах, доступных лично вам, и которые я настоятельно рекомендую внедрить в свою практику всем разработчикам.


Скрипты установки


Команда npm install поддерживает ряд скриптов (например: preinstall, install/postinstall), которые могут выполняться в процессе установки пакета. При этом содержимое таких скриптов ничем не ограничивается и выполняется от лица пользователя, выполняющего установку. Такие скрипты являются одним из основных способов для злоумышленников влезть в компьютер, на котором устанавливается вредоносный пакет, и похитить важную информацию.


Пример пакета, содержащего вредоносный скрипт:


Пример пакета содержащего вредоносный скрипт


Если пользователь выполнит команду установки пакета из примера выше: npm install malicious-package, то это приведет к выполнению скрипта evil.sh, который потенциально может совершить любые действия от лица текущего unix-пользователя.


Возможно, вы слышали, что в середине 2018 года произошел громкий инцидент с пакетами eslint-scope и eslint-config-eslint, когда злоумышленник смог получить к ним доступ и опубликовать новую версию пакетов, содержащую вредоносный код в скриптах установки.

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

Чтобы изолировать проблему, команде npm пришлось отозвать все токены npm, которые были сгенерированы до даты начала атаки. Это привело к тому, что npm registry начал испытывать проблемы с доступностью, потому что пользователи начали массово перевыпускать токены, не говоря о том, что это нарушило работу огромного количества автоматизированных конвейеров, которые обращались к npm с использованием токенов.

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

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


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


npm install suspicious-package --ignore-scripts

Также, вы можете полностью запретить выполнение скриптов установки при помощи следующей команды (или добавить настройку в .npmrc):


npm config set ignore-scripts true

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


Ограничение среды выполнения


Ограничение среды выполнения


Чтобы дополнительно обезопасить себя от подобных атак, постарайтесь максимально ограничить среду, в которой выполняются команды npm, такие как npm install. Как вариант, их можно выполнять внутри Docker-контейнера, на виртуальной машине или в любой другой песочнице с ограниченным доступом. Разумеется, никогда не стоит запускать npm от лица root-пользователя; наоборот, лучше запускать эти команды от лица пользователя с минимальным доступом и набором прав. Антивирусы и брандмауэры также могут сократить риски. Принцип прост: чем меньше полномочий получит процесс npm, тем безопаснее будет его работа.


Также полезно будет настроить систему предупреждений: если вдруг процесс начнет совершать какие-то подозрительные действия, то вам следует об этом знать. Выявление угрозы безопасности поможет не только вам и вашей команде или компании, но и всему сообществу в целом. Поэтому не забудьте составить и отправить отчет о найденной уязвимости в npm.


Безопасность токенов и ключей


Безопасность токенов и ключей


Первое правило при работе с ключами доступа и токенами: никогда не храните их в открытом доступе, если этого можно избежать. К примеру, не стоит хранить токен npm в файле .npmrc на вашей машине, если можно аутентифицировать в интерактивном режиме.


Если вам всё же необходимо использовать токен аутентификации npm в каком-то автоматическом конвейере (например, для CI/CD), то ограничьте его полномочия. Например, если токен нужен только для установки пакетов из приватного репозитория, то создавайте его в режиме read-only, чтобы в случае его утечки злоумышленник не мог отправить вредоносный код в ваш репозиторий. Это позволит ограничить масштаб угрозы.


Также хорошей практикой является привязка токена аутентификации npm к определенному диапазону IP-адресов (CIDR). Закажите себе статический IP (или серию IP) и привяжите его к своему серверу. Как правило, любой облачный провайдер позволяет это сделать, даже если ваш сервер запускается по запросу (on-demand). Таким образом, если злоумышленник сможет украсть токен, то он не сможет использовать его с другой машины.


Не размещайте токен аутентификации в файле .npmrc, и тем более не добавляйте его в систему контроля версий (Git). Вместо этого используйте переменную окружения NPM_TOKEN в связке со специальным защищенным хранилищем для токенов (изучите документацию вашего провайдера).


Сгенерировать новый токен можно при помощи команды npm token create, которая опционально принимает следующие аргументы:


  • --read-only создаёт токен, который позволяет только считывать данные из репозитория (т. е. устанавливать пакеты, но не публиковать их);
  • --cidr=<CIDR> создаёт токен, который позволяет аутентифицироваться только хостам в указанном диапазоне IP. Рекомендую использовать калькулятор CIDR, чтобы быть уверенными в корректности задания диапазона IP. Пример: --cidr=192.0.2.0/24.

Также рекомендуется регулярно проводить аудит всех токенов, привязанных к npm-аккаунту, и отзывать ненужные. Посмотреть список всех токенов можно в личном кабинете на сайте npm, либо при помощи команды npm token list.


Учтите, что аутентификация при помощи команды npm adduser приравнивается к созданию токена и сохранению его в файле .npmrc в домашней директории пользователя. Удаление же токена приведет к разлогиниванию пользователя.


Аутентификация в файле ~/.npmrc выглядит следующим образом:


//registry.npmjs.org/:_authToken=00000000-0000-0000-0000-000000000000

Если вы используете различные независимые npm registry, то таких строк может быть несколько.


Убедитесь, что содержимое файла ~/.npmrc доступно только вашему unix-пользователю!

Что касается закрытых ключей шифрования (например, для SSH), то убедитесь, что они защищены сильным паролем и используют надежный алгоритм шифрования. В случае компрометации такого ключа злоумышленник не сможет им воспользоваться, т. к. не знает пароль, а методы математического взлома окажутся неэффективными. В случае с SSH, в настоящее время алгоритм EdDSA считается наиболее надежным. Однако стоит учесть, что старые системы могут его не поддерживать. Тогда используйте два ключа: Ed25519 (EdDSA) для всех совместимых систем и RSA (с минимум 2048 битным ключом) для устаревших систем.


Очепятки


Очепятки


Одним из старейших приемов в рукаве злоумышленников является использование названий, похожих на оригинальные, но отличающихся одним или несколькими символами. Пользователи часто совершают опечатки при вводе тех или иных названий. Особенно это критично при вызове команды, например, npm install packae-name: в лучшем случае это приведет к ошибке 404, а в худшем может вызвать вредоносный код.


Команда npm старается автоматически вычислять попытки подобного подлога и блокировать пакеты, которые выдают себя за другие. Вам же, как пользователю, следует просто внимательнее относиться к вводу названий пакетов с клавиатуры, по возможности пользуйтесь функцией copy-and-paste.


Безопасные пароли


Безопасные пароли


Про правила использования безопасных паролей сказано уже очень много, не вижу смысла повторяться, однако отмечу основные моменты:


  • используйте уникальные длинные пароли с буквами в разном регистре, цифрами и спец. символами (16 символов будет достаточно);
  • не используйте один и тот же пароль более чем в одном месте. Для каждого ресурса создавайте новый пароль;
  • храните пароли только в защищенном месте и передавайте только по зашифрованному соединению;
  • у каждого пользователя должен быть свой именной логин и пароль, не используйте одни и те же реквизиты более чем для одного человека.

Для решения всех вышеописанных вопросов стоит использовать специальное ПО: менеджер паролей. Я использую открытый KeePass (есть версии практически для любой платформы). Базу данных паролей стоит защитить сложным мастер-паролем, который вам необходимо запомнить. Дополнительно можно использовать файл-ключ, который хранится, например, на флешке. Сам же файл базы данных можно положить в любое облачное хранилище (Яндекс.Диск, Google Drive или DropBox), чтобы иметь к нему синхронизированный доступ сразу со всех устройств.


Мой коллега недавно перевел статью, в которой подробнее разбирается вопрос оптимальной длины пароля, рекомендую ознакомиться!

Мультифакторная аутентификация (MFA)


Мультифакторная аутентификация (MFA)


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


К счастью, npm также поддерживает двухфакторную аутентификацию при помощи программ-аутентификаторов. Она работает в двух режимах: только аутентификация или аутентификация и запись. Лучше всего использовать режим аутентификация и запись, т. к. это гарантирует самый высокий уровень безопасности, потому что npm будет просить вас ввести токен OTP не только при вызове команды npm adduser, но и при любой попытке записать что-то в реестр (например, при вызове npm publish).


О том, как включить npm 2FA, написано подробно в официальной документации. Процесс в целом ничем не отличается от стандартного: вам нужно выбрать режим работы, а затем просканировать QR-код, который будет показан на экране при помощи выбранной вами программы-аутентификатора. Сделать это можно как через личный кабинет на официальном сайте, так и через CLI при помощи команд:


  • npm profile enable-2fa auth-and-writes
    режим аутентификация и запись (рекомендуется)
  • npm profile enable-2fa auth-only
    режим только аутентификация

При использовании CLI npm попросит вас ввести пароль от аккаунта (даже если вы уже аутентифицированы), а затем покажет QR-код. После сканирования кода нужно ввести одноразовый пароль (OTP), который покажет приложение на устройстве, чтобы подтвердить успешность процедуры.


При привязке аутентификатора npm также выдаст вам набор одноразовых кодов восстановления, их необходимо сохранить в надежном месте, на случай, если придется восстанавливать доступ к аккаунту при утере аутентификатора.


Если утеря всё же произошла, вы можете ввести неиспользованный ранее код восстановления вместо одноразового пароля при аутентификации, а затем сбросить 2FA командой npm profile disable-2fa, введя затем еще один код восстановления. Если же вы потеряли и аутентификатор, и коды восстановления, то вам придется обратиться в службу поддержки npm (надеюсь, они защищены от социальной инженерии).


Ограничение публикуемых файлов


Ограничение публикуемых файлов


По умолчанию, когда вы выполняете команду npm publish, npm собирает в архив все файлы в директории пакета за исключением некоторых.


Полный список файлов, которые npm никогда не добавляет в архив пакета
  • .git
  • CVS
  • .svn
  • .hg
  • .lock-wscript
  • .wafpickle-N
  • .*.swp
  • .DS_Store
  • ._*
  • npm-debug.log
  • .npmrc
  • node_modules
  • config.gypi
  • *.orig
  • package-lock.json

Часто из-за этого в registry утекают файлы, которые не должны быть опубликованы, например, файлы с паролями. Это серьезная угроза безопасности. Если вы случайно отправили в реестр npm какие-то чувствительные файлы, то вам придется не только удалить архив пакета из реестра, но и полностью заменить все скомпрометированные данные.


А чтобы обезопасить себя от подобных утечек, я рекомендую всегда использовать поле files в файле package.json. Оно представляет собой массив, содержащий перечисление файлов (можно использовать шаблоны glob), которые должны попасть в архив собранного пакета. Такой явный подход гарантирует, что в публичный доступ случайно не утекут какие-то нежелательные файлы.


Нужно заметить, что следующие файлы всегда попадают в архив, даже если не перечислены в массиве files:


  • package.json
  • README
  • CHANGES, CHANGELOG, HISTORY
  • LICENSE, LICENCE
  • NOTICE
  • Файл, указанный в поле main

При этом файлы README, CHANGES, LICENSE и NOTICE могут иметь любой регистр символов в названии и расширение.


По этой причине важно, чтобы перечисленные выше файлы никогда не содержали чувствительной информации.


Чтобы проверить, что же попадет в архив при запуске команды npm publish, можно использовать флаг --dry-run либо команду npm pack --dry-run. Вторая команда, в отличие от первой, пропустит скрипты препаблишинга и сразу попытается собрать архив. Аргумент --dry-run гарантирует, что изменения будут произведены в тестовом режиме только на вашей машине, пакет не будет никуда отправляться, и архив не будет реально создан в файловой системе.


Продолжение следует


В этом посте мы начали рассматривать инструменты и подходы, которыми каждый из нас может пользоваться, чтобы повысить безопасность своего npm-проекта. Я настоятельно рекомендую всегда иметь эти аспекты в виду и внедрить описанные инструменты у себя в наше сложное время дополнительные меры защиты никогда не будут лишними.


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




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


Если же у вас есть вопросы или желание что-то добавить по теме, то не стесняйтесь оставлять комментарии, я с радостью приму участие в обсуждении и учту ваши пожелания в следующих постах.


Все изображения в статье являются собственностью своих авторов или законных правообладателей и используются исключительно в некоммерческих образовательных целях.

Подробнее..

SEO npm-пакета почему важно правильно настраивать конфиг и писать тесты

07.09.2020 02:17:11 | Автор: admin

Не так давно я опубликовал статью о своем CLI для React-компонент, который для меня стал первым публичным npm-пакетом. И так как мне хотелось поделиться своими наработками с как можно большим кругом разработчиков я начал изучать разные способы улучшения своей позиции в поисковой выдаче на разных специализированных сайтах. В попытках улучшить свое положение я опирался на поиск в npm, yarn и npms. И если вы сейчас откроете страничку моего пакета в любом из этих трех сайтов, то результаты там будут, к сожалению, достаточно скромными и я попробую объяснить почему и порассуждаю на эту тему.

Popularity, quality, maintainance

Если мы сделаем поиск в npm по любому запросу, то напротив каждого из пакетов будет указан набор из трех характеристик: popularity, quality и maintainance. Они в каком-то роде напоминают значения по пирам и сидам на каком-нибудь торрент-трекере и в достаточной мере влияют на выдачу и на последующий выбор людей.

Popularity рейтинг достаточно очевидный и связан напрямую со скачиванием пакета, со звездочками на гитхабе, с количеством форков и подписчиков у автора. Все подробности можно почитать на сайте npms.

Так как у нас пакет новый то popularity у нас само собой будет на нуле, поэтому будем смотреть на остальные два рейтинга. Quality мы оставим на десерт, а сначала поговорим о maintainance. И тут все достаточно просто, он отображает состояние пакета с точки зрения разработки. Либо его активно разрабатывают и поддерживают, либо на него забили и не занимаются. Тут учитывается то, как часто происходят коммиты, релизы, насколько много issue и как быстро они закрываются. В целом вроде все просто, но если в npms у меня этот показатель заполнен на 100%, потому что я активно дорабатываю пакет и прикручиваю новые фишки почти каждую неделю, то в npm у меня этот рейтинг только 33%. И как бы я не старался, выше он не поднимается. Более того, если взять любой другой популярный генератор кода, то окажется что все эти пакеты имеют данный рейтинг тоже равный 33%, что выглядит немного подозрительным. Даже у самого React этот рейтинг такой же.

Popularity у нас на нуле, maintainance на максимум, а что с quality? А тут все гораздо интереснее. Изначально я писал свою CLI на чистом js и без тестов. Но к тому моменту, когда я решил вытащить её в публичное поле я уже переписал её на Typescript и более или менее причесал, но все еще тестов не было. И я точно не помню сколько у меня был этот показатель качества, но думаю был примерно в районе 20%. Сейчас же я его разогнал до 61% и могу немного рассказать, как этого можно добиться. Причем я пытался применять сразу несколько практик одновременно, поэтому я до конца не уверен все ли они влияют, но даже если нет, то в целом это стоит того чтобы добавить в свой проект, если вы тоже решитесь опубликовать пакет.

У меня получился следующий список настроек:

  1. В проекте должен быть настроен линтер. Я лично использую ESLint, возможно с TSLint пакетные менеджеры тоже нормально работают.

  2. Стоит написать хороший readme.mdи changelog.md и поддерживать их в актуальном состоянии

  3. Стоит добавить файл лицензии

  4. Не лишним будет добавить файл .editorconfig Не факт что оно влияет на рейтинг, но пригодиться если кто-то решит вам помочь с вашим пакетом. Хотя и линтер тоже с этим поможет.

  5. Также я подключил свой проект на github к Travis и создал для него конфиг с помощью файла .travis.yml. Когда я его подключал я преследовал исключительно цель попробовать подняться в рейтинге, однако это оказалось достаточно неплохим инструментом тестирования. Более того, в моем CLI очень важно чтобы все корректно работало как на Linux так и на Windows и для меня оказалось приятной неожиданностью, когда Travis прогнал мои тесты у себя на Linux и я поймал баг, о котором совсем не знал, потому что разрабатываю под виндой.

  6. Не уверен что это влияет на рейтинг, но достаточно важно корректно заполнить файл package.json. Указать в нем все скрипты, главный файл, описание и теги.

Ну и конечно помимо банальных настроек проекта нужно писать тесты. И чтобы рейтинг поднялся высоко, покрытие должно быть очень высоким. Как я уже сказал выше, итоговый рейтинг в npm у меня вышел равным 61%, при этом покрытие тестами у меня всего лишь 49%, ну и есть все эти настройки, указанные выше. На nmps все получше, там у меня 96%. Само собой, в первую очередь, я покрыл критическую функциональность пакета, поскольку с каждой новой фичей тестировать все кейсы становится все сложнее и сложнее, хотя в некоторых случаях я откровенно читерил и покрывал тестами файлы, в которых максимально сложно совершить ошибку, но зато тесты пишутся легко и покрытие растет очень дешево.

Цифры или PR

Ну хорошо, настроили мы проект, пушим в него часто и фиксим оперативно все баги, покрываем тестами, но что-то никто не приходит скачивать наш пакет. Открываем поиск того же npm, вбиваем релевантный запрос для нашего пакета и начинаем листать страницы: 1, 2, 3,... 21. Находим наш пакет на какой-нибудь очень далекой странице и как нам понять что пошло не так?

Начну, пожалуй, с одной забавной истории про одно поле. Когда я всеми силами пытался вытащить свой пакет в первые строчки yarn, я настраивал проект, писал тесты, выбирал теги получше и улучшал readme. Писал посты на reddit и пиарил пакет среди коллег. Из кожи вон лез, но пакет качали очень слабо, где-то скачиваний 20-30 в день было. И хоть даже это меня радовало, хотелось узнать, как на это повлиять и я начал смотреть что есть в пакетах конкурентов. Многие пакеты в поиске по релевантному для меня запросу были вообще не подходящими по сути и делали совершенно не то, что я как бы пытаюсь искать, но тем не менее были выше и я пытался выяснить почему. Первое что достаточно сомнительно выстреливает, когда мы говорим о поиске - это то что мелкие, крайне бесполезные пакеты, в которых 150 строк кода, покрытых тестами, вылезают в топ за счет невероятно высокого покрытия. Часто бывало так что у меня только index файл был длиннее чем пакеты, которые обходили меня в рейтинге, хотя при этом и популярными они тоже не были, потому что такой пакет может написать каждый за парочку часов. И вот я натыкаюсь на очередной пакет, который крайне маленький, бесполезный, но почему-то находящийся выше в yarn. Стало очень любопытно и я начал проглядывать каждый файл репозитория и сравнивать настройки со своими. И тут я вижу что единственное отличие моей репки, от репки конкурента - это поле description в package.json. Ну, я подумал, что вряд ли это мне как-то поможет, но почему бы его не добавить, а то я как-то про него забыл. В общем добавляю поле и на следующий день бац и +200 скачиваний, потом еще больше и еще. Более того, по одному из запросов в yarn я находился на 23 станице до которой вряд ли бы кто-то дошел, пытаясь найти нужный пакет, но, указав это поле, пакет оказался буквально на первой строчке поисковика.

После такого успеха и роста до двух тысяч скачиваний в месяц и почти тысячи скачиваний за неделю, в одном из моментов, я подумал, что, кажется, я поддал нужного количества углей в эту PR печку и оно дальше как-то само пойдет. Тем более я заходил в соседние пакеты, поглядывал на их issue и с довольным видом продолжал работать над своим продуктом, потому что у меня эти фичи были реализованы из коробки. Но шло время, и весь этот мощный буст превратился в невероятный спад. С каждым днем кривая скачиваний спускалась все ниже, и ниже и я пытался разобраться в том что сделал не так, но точного понимания этого у меня до сих пор нет.

В каком-то смысле все эти настройки пакета реально работают и улучшают позицию в разного рода выдачах, с другой стороны в чистом виде оно тоже работает плохо и даже с учетом того что спустя время у меня появились и звезды на github и целая гора новых фич, оно все еще где-то висит далеко не на первых строчках в поиске, все еще очень слабо скачивается. И кажется, что все-таки сам по себе поиск плохо работает и чтобы пакет стал реально популярным, нужно хорошо так его прорекламировать. Так что если вдруг соберетесь что-то подобное разрабатывать, то готовьтесь к чему-то такому.

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

Кто-то может сказать, что это из-за того что эта функциональность никому не нужна. И этот кто-то тоже окажется не прав, поскольку есть другие пакеты, которые решают схожую задачу, далеко не всегда лучше, но при этом имеют 15 тысяч скачиваний в месяц и более. Хотя, конечно там более старые, известные проекты, которые имеют не одну публикацию в интернете и много коллабораторов на github.

В целом я не удивлен что все работает именно так, поскольку если бы я зашел на страничку пакета у которого 150 скачиваний в неделю, я бы скорее всего тоже даже описание не начал бы читать, поскольку все мы привыкли доверять чему-то очень популярному и проверенному, даже если у этого чего-то 200 issue.

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

Подробнее..

Из песочницы Nested Sets для Javascript

15.09.2020 22:15:12 | Автор: admin
На любом современном сайте (да и на сайтах постарше) встречаются вложенные структуры, иерархия объектов, деревья. Самый распространенный пример каталог.

Сегодня множество проектов разрабатывается с использованием Javascript. Как же хранить древовидные структцры в этом случае? Об этом и хотелось бы поговорить.

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

Существуют различные алгоритмы для хранения деревьев и примером таких алгоритмов могут послужить Adjacency List, Matherialized Path, Nested Set и Closure Table.

Если посоветуете еще какие-то буду рад услышать и поучиться.

Когда я писал расширения для Joomla я часто использовал Nested Set. Именно в этой CMS я впервые встретил эту модель. Но теперь стек поменялся и сейчас это Javascript. Привычки остались, да и сайты на Joomla тоже. Нужно переносить данные на новые сервисы и проекты.

О Nested Sets довольно много информации в интернете и при желании вы всегда сможете ее найти, но тем не менее пару слов о этой модели данных я должен сказать.

Смысл Nested Set в том, что у каждого узла в иерархии существует пара ключей левый ключ и правый ключ. В зависимости от их значений осуществляется обход дерева. Положительными качествами алгоритма, на мой взгляд, является быстрота выборки данных. В этом алгоритме нет рекурсии. В то же время для изменения структуры дерева, добавления, удаления и переноса узлов, необходимо пересчитывать все ключи.

Чтобы использовать данные из Nested Set в проектах на Javascript нужен модуль который умел бы работать с этой моделью.

Поискав через npm я нашел модули, функционалом которых была выборка данных из структуры Nested Sets, т.е. все ключи уже должны были быть проставлены. Была необходимость править структуру, но такой возможности я не нашёл.

Еще одной проблемой является то, что в большинстве случаев и данные и структура дерева хранятся в одной сущности, но на мой взгляд гораздо эффективнее разделять эти вещи.

Таким образом одна и та же категория (данные категории) может лежать в разных родителях. Это позволит пользователям находить то, что они ищут быстрее при хорошо продуманной иерархии.

Хотя с точки зрения SEO появятся две страницы с разными URL и с одинаковым контентом, но это можно решить каноническими ссылками.

Если это не правильно прошу SEO-специалистов меня поправить.

В итоге я решил написать модуль и опубликовать его на npmjs.com.

Если кому-то пригодится буду очень рад.

Сейчас я продолжаю работать над ним и в планах реализовать перенос узла по дереву.

Вот ссылка на npm, где вы можете скачать пакет.

Вот ссылка на github, где вы можете скачать исходники.

Документация есть и там и там.

Буду рад любым комментариям.

Хороших вам проектов и интересных задач.
Подробнее..

Основные команды bash, git, npm и yarn, а также немного о package.json и semver

05.10.2020 14:22:06 | Автор: admin
Доброго времени суток, друзья!

Предлагаю вашему вниманию небольшую шпаргалку по основным командам bash, git, npm, yarn, package.json и semver.

Условные обозначения: [dir-name] означает название директории, | означает или.

Рекомендую вводить каждую команду в терминале и внимательно изучать вывод, так вы быстро их запомните и определите, какие команды вам нужны, а какие нет.

Без дальнейших предисловий.

Оглавление:


bash


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

Установка: в моем случае bash был установлен вместе с git.

Справка:

help

История команд:

history

Очистка терминала:

clear

Выход из терминала:

exit

Создание директории:

// make directorymkdir [dir-name]// примерmkdir my-app// несколько диреторийmkdir -p {dir1,dir2}// несколько вдложенных директорийmkdir -p my-app/{css,js}

Смена директории:

// change directorycd [dir-name]// примерcd my-app// сразу после созданияcd !$// родительская директорияcd ..// на два уровня вышеcd ../..// предыдущая директорияcd -// домашняя директорияcd ~

Путь к текущей директории:

// print work directorypwd

Список файлов:

// listls// включая скрытые файлыls -a | -f// больше информации// например, права доступаls -l

Создание файла:

touch [file-name]// примерtouch index.html// несколько файловtouch my-app/{index.html,css/style.css,js/script.js}

Содержимое файла:

cat [file-name]// примерcat index.html// сортировка и выборка уникальных значенийcat [file-name] | sort | uniq// меньше контентаless [file-name] // q - exit// n строк с начала файлаhead -50 [file-name]// n строк с конца файлаtail -50 [file-name]// поиск словаgrep [string] [file-name]// распаковка и просмотр содержимого архиваunzip [achive-name]// тип файлаfile [file-name]

Копирование, перемещение и удаление файла:

// copycp [file1] [file2]// movemv [file1] [file2]// пример// перемещение всех файлов из одной директории в другуюmv [dir1]/*.* [dir2]// removerm [file-name]// удаление пустой директорииrmdir [dir-name]// удаление непустой директорииrm -r [dir-name]// илиrm -rf [dir-name]

Вывод в терминал строки:

echo [string]// примерecho hello// создание или перезапись файлаecho hello > greet.txt// добавление строки в файлecho hello >> greet.txt

Загрузка файла:

wget [url]

Коннекторы:

true && echo hellofalse || echo helloecho hello ; ls

Конвейер:

// количество переносов строки - \ncat [file] | wc -l

git


git представляет собой распределенную систему контроля версий, позволяющую контролировать процесс внесения изменений в проект.

Книга Pro Git.

Скринкаст Ильи Кантора.

Быстрый старт: Git How To.

Установка: git-scm.com.

Проверка установки:

git --version

Справка:

git helpgit help [command-name]git [command-name] --help | -h

Минимальные настройки:

// --local - настройки для текущего репо// --global - настройки для текущего пользователя// --system - настройки для всей системы, т.е. для всех пользователейgit config --global user.name "My Name"git config --global user.email "myemail@example.com"

Дополнительные настройки:

// список глобальных настроекgit config --list | -l --global// редактирование глобальных настроекgit config --global --edit | -e

Создание репозитория:

git init

Очистка репозитория:

// -d - включая директории, -x - включая игнорируемые файлы, -f - принудительнаяgit clean | -dxf

Удаление файлов и директорий:

// removegit rm [file-name]git rm -r [dir-name]git rm --force | -f

Перемещение файлов:

// git add + git remove// movegit mv [old-file] [new-file]

Просмотр состояния репозитория:

git status

Добавление изменений:

git add [file-name]git add --force | -f// все файлыgit add . | --all | -A// для добавления пустой директории можно создать в ней пустой файл .gitkeep

Добавление сообщения (коммита):

// редактирование коммитаgit commit// коммит для одного изменения, если не выполнялось git add . | -A// если выполнялось, сообщение будет добавлено для всех измененийgit commit --message | -m "My Message"// для всех изменений, если git add [file-name] выполнялось несколько разgit commit --all | -a -m | -am "My Message"// исправление коммитаgit commit --amend "My Message" | --no-edit

Просмотр коммита:

// последний коммитgit show// другой коммитgit show [hash] // минимум первые 4 символа// поиск изменений по сообщению или части сообщенияgit show :/[string]// поиск коммита по тегуgit show [tag-name]

Просмотр разницы между коммитами:

git diff HEAD | @ // HEAD - как правило, текущая ветка; @ - алиас для HEAD// stagedgit diff --staged | --cachedgit diff [hash1] [hash2]// разница между веткамиgit diff [branch1]...[branch2]// просмотр разницы между коммитами при редактировании сообщенияgit commit --verbose | -v// кастомизация выводимого сообщенияgit diff --word-diff | --color-words

Просмотр истории изменений:

git log// n - количество измененийgit log -n// --since, --after - после// --until, --before - до// разницаgit log -p// быстрое форматированиеgit log --graph --oneline --stat// кастомное форматированиеgit log --pretty=format// примерgit log --pretty=format:'%C(red)%h %C(green)%cd %C(reset)| %C(blue)%s%d %C(yellow)[%an]' --date=short | format-local:'%F %R'// поиск изменений по слову, файлу, ветке; i - без учета регистраgit log --grep | -G [string] | [file] | [branch] & -i// поиск по нескольким строкамgit log --grep [string1] --grep [string2] --all-match// поиск в определенном блоке файлаgit log -L '/<head>/','/<\/head>/':index.html// поиск по авторуgit log --author=[name]

Отмена изменений:

git reset// --hard - включая рабочую директорию и индекс// --soft - без рабочей директории и индекса// --mixed - по умолчанию: без рабочей директории, но с индексомgit reset --hard [hash] | @~ // @~ - последний коммит в HEAD// аналогичноgit reset --hard ORIG_HEAD// не путать с переключением веткиgit checkoutgit restore

Работа с ветками:

// список ветокgit branch// создание веткиgit branch [branch-name]// переключение на веткуgit checkout [branch-name]// branch + checkoutgit checkout -b [branch-name]// переименованиеgit branch -m [old-branch] [new-branch]// удаление веткиgit branch -d [branch-name]// слияние ветокgit merge [branch-name]

Разрешение конфликтов при слиянии:

// обычно, при возникновении конфликта, открывается редактор// принять изменения из сливаемой веткиgit checkout --ours// принять изменения из текущей веткиgit checkout --theirs// отмена слиянияgit reset --mergegit merge --abort// получение дополнительной информацииgit checkout --conflict=diff3 --merge [file-name]// продолжить слияниеgit merge --continue

Удаленный репозиторий:

// клонированиеgit clone [url] & [dir]// просмотрgit remotegit remote showgit remote add [shortname] [url]git remote rename [old-name] [new-name]// получение изменений// git fetch + git mergegit pull// отправка измененийgit push

Теги:

// просмотрgit tag// легковесная меткаgit tag [tag-name]//примерgit tag v1-beta// аннотированная меткаgit tag -a v1 -m "My Version 1"// удалениеgit tag -d [tag-name]

Отладка

git bisectgit blamegit grep

Сохранение незакоммиченных изменений:

// сохранениеgit stash// извлечениеgit stash pop

Копирование коммита:

git cherry-pick | -x [hash]// если возник конфликт// отменаgit cherry-pick --abort// продолжитьgit cherry-pick --continuegit cherry-pick --no-commit | -n// --cherry = --cherry-mark --left-right --no-mergesgit log --oneline --cherry [branch1] [branch2]

Перебазирование:

git rebase [branch]// при возникновении конфликта// отменаgit rebase --abort// пропуститьgit rebase --skip// продолжитьgit rebase --continue// предпочтение коммитов слиянияgit rebase --preserve-merges | -p// интерактивное перебазированиеgit rebase -i [branch]

Автозавершение повторных конфликтов:

// rerere - reuse recorder resolution// rerere.enabled true | false// rerere.autoUpdate true | false// rerere-train.sh - скрипт для обучения rereregit rerere forget [file-name]

Обратные коммиты:

git revert @ | [hash]// отмена слияния// git reset --hard @~ не сработаетgit revert [hash] -m 1// git merge [branch] не сработает// отмена отменыgit revert [hash]// повторное слияние с rebasegit rebase [branch1] [branch2] | --onto [branch1] [hash] [branch2]git merge [branch]git rebase [hash] --no-ff

Пример алиасов (сокращений) для .gitconfig:

[alias]    aa = add -A    co = checkout    ci = commit -m    st = status    br = branch

Пример .gitconfig:
[user]name = [My Name]email = [myemail@example.com]username = [myusername][core]editor = [myeditor]whitespace = fix,-indent-with-non-tab,trailing-space,cr-at-eolpager = delta[web]browser = google-chrome[instaweb]httpd = apache2 -f[rerere]enabled = 1autoupdate = 1[push]default = matching[color]ui = auto[color "branch"]current = yellow boldlocal = green boldremote = cyan bold[color "diff"]meta = yellow boldfrag = magenta boldold = red boldnew = green boldwhitespace = red reverse[color "status"]added = green boldchanged = yellow bolduntracked = red bold[difftool]prompt = false[delta]features = line-numbers decorationsline-numbers = true[delta "decorations"]minus-style = red bold normalplus-style = green bold normalminus-emph-style = white bold redminus-non-emph-style = red bold normalplus-emph-style = white bold greenplus-non-emph-style = green bold normalfile-style = yellow bold nonefile-decoration-style = yellow boxhunk-header-style = magenta boldhunk-header-decoration-style = magenta boxminus-empty-line-marker-style = normal normalplus-empty-line-marker-style = normal normalline-numbers-right-format = "{np:^4} "[github]user = [username]token = token[gitflow "prefix"]versiontag = v[sequence]editor = interactive-rebase-tool[alias]a = add --allai = add -i###ap = applyas = apply --statac = apply --check###ama = am --abortamr = am --resolvedams = am --skip###b = branchba = branch -abd = branch -dbdd = branch -Dbr = branch -rbc = rev-parse --abbrev-ref HEADbu = !git rev-parse --abbrev-ref --symbolic-full-name "@{u}"bs = !git-branch-status###c = commitca = commit -acm = commit -mcam = commit -amcem = commit --allow-empty -mcd = commit --amendcad = commit -a --amendced = commit --allow-empty --amend###cl = clonecld = clone --depth 1clg = !sh -c 'git clone git://github.com/$1 $(basename $1)' -clgp = !sh -c 'git clone git@github.com:$1 $(basename $1)' -clgu = !sh -c 'git clone git@github.com:$(git config --get user.username)/$1 $1' -###cp = cherry-pickcpa = cherry-pick --abortcpc = cherry-pick --continue###d = diffdp = diff --patiencedc = diff --cacheddk = diff --checkdck = diff --cached --checkdt = difftooldct = difftool --cached###f = fetchfo = fetch originfu = fetch upstream###fp = format-patch###fk = fsck###g = grep -p###l = log --onelinelg = log --oneline --graph --decorate###ls = ls-fileslsf = !git ls-files | grep -i###m = mergema = merge --abortmc = merge --continuems = merge --skip###o = checkoutom = checkout masterob = checkout -bopr = !sh -c 'git fo pull/$1/head:pr-$1 && git o pr-$1'###pr = prune -v###ps = pushpsf = push -fpsu = push -upst = push --tags###pso = push originpsao = push --all originpsfo = push -f originpsuo = push -u origin###psom = push origin masterpsaom = push --all origin masterpsfom = push -f origin masterpsuom = push -u origin masterpsoc = !git push origin $(git bc)psaoc = !git push --all origin $(git bc)psfoc = !git push -f origin $(git bc)psuoc = !git push -u origin $(git bc)psdc = !git push origin :$(git bc)###pl = pullpb = pull --rebase###plo = pull originpbo = pull --rebase originplom = pull origin masterploc = !git pull origin $(git bc)pbom = pull --rebase origin masterpboc = !git pull --rebase origin $(git bc)###plu = pull upstreamplum = pull upstream masterpluc = !git pull upstream $(git bc)pbum = pull --rebase upstream masterpbuc = !git pull --rebase upstream $(git bc)###rb = rebaserba = rebase --abortrbc = rebase --continuerbi = rebase --interactiverbs = rebase --skip###re = resetrh = reset HEADreh = reset --hardrem = reset --mixedres = reset --softrehh = reset --hard HEADremh = reset --mixed HEADresh = reset --soft HEADrehom = reset --hard origin/master###r = remotera = remote addrr = remote rmrv = remote -vrn = remote renamerp = remote pruners = remote showrao = remote add originrau = remote add upstreamrro = remote remove originrru = remote remove upstreamrso = remote show originrsu = remote show upstreamrpo = remote prune originrpu = remote prune upstream###rmf = rm -frmrf = rm -r -f###s = statussb = status -s -b###sa = stash applysc = stash clearsd = stash dropsl = stash listsp = stash popss = stash savessk = stash save -ksw = stash showst = !git stash list | wc -l 2>/dev/null | grep -oEi '[0-9][0-9]*'###t = tagtd = tag -d###w = showwp = show -pwr = show -p --no-color###svnr = svn rebasesvnd = svn dcommitsvnl = svn log --oneline --show-commit###subadd = !sh -c 'git submodule add git://github.com/$1 $2/$(basename $1)' -subrm = !sh -c 'git submodule deinit -f -- $1 && rm -rf .git/modules/$1 && git rm -f $1' -subup = submodule update --init --recursivesubpull = !git submodule foreach git pull --tags origin master###assume = update-index --assume-unchangedunassume = update-index --no-assume-unchangedassumed = !git ls -v | grep ^h | cut -c 3-unassumeall = !git assumed | xargs git unassumeassumeall = !git status -s | awk {'print $2'} | xargs git assume###bump = !sh -c 'git commit -am \"Version bump v$1\" && git psuoc && git release $1' -release = !sh -c 'git tag v$1 && git pst' -unrelease = !sh -c 'git tag -d v$1 && git pso :v$1' -merged = !sh -c 'git o master && git plom && git bd $1 && git rpo' -aliases = !git config -l | grep alias | cut -c 7-snap = !git stash save 'snapshot: $(date)' && git stash apply 'stash@{0}'bare = !sh -c 'git symbolic-ref HEAD refs/heads/$1 && git rm --cached -r . && git clean -xfd' -whois = !sh -c 'git log -i -1 --author=\"$1\" --pretty=\"format:%an <%ae>\"' -serve = daemon --reuseaddr --verbose --base-path=. --export-all ./.git###behind = !git rev-list --left-only --count $(git bu)...HEADahead = !git rev-list --right-only --count $(git bu)...HEAD###ours = "!f() { git checkout --ours $@ && git add $@; }; f"theirs = "!f() { git checkout --theirs $@ && git add $@; }; f"subrepo = !sh -c 'git filter-branch --prune-empty --subdirectory-filter $1 master' -human = name-rev --name-only --refs=refs/heads/*[filter "lfs"]clean = git-lfs clean -- %fsmudge = git-lfs smudge -- %fprocess = git-lfs filter-processrequired = true


Пример .gitignore:
### Node #### Logslogsnpm-debug.log*yarn-debug.log*yarn-error.log*# Optional npm cache directory.npm# Dependency directories/node_modules/jspm_packages/bower_components# Yarn Integrity file.yarn-integrity# Optional eslint cache.eslintcache# dotenv environment variables file(s).env.env.*#Build generateddist/build/# Serverless generated files.serverless/### SublimeText #### cache files for sublime text*.tmlanguage.cache*.tmPreferences.cache*.stTheme.cache# workspace files are user-specific*.sublime-workspace# project files should be checked into the repository, unless a significant# proportion of contributors will probably not be using SublimeText# *.sublime-project### VisualStudioCode ###.vscode/*!.vscode/settings.json!.vscode/tasks.json!.vscode/launch.json!.vscode/extensions.json### Vim ###*.sw[a-p]### WebStorm/IntelliJ ###/.ideamodules.xml*.ipr*.iml### System Files ###*.DS_Store# Windows thumbnail cache filesThumbs.dbehthumbs.dbehthumbs_vista.db# Folder config fileDesktop.ini# Recycle Bin used on file shares$RECYCLE.BIN/# Thumbnails._*# Files that might appear in the root of a volume.DocumentRevisions-V100.fseventsd.Spotlight-V100.TemporaryItems.Trashes.VolumeIcon.icns.com.apple.timemachine.donotpresent


npm


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

Официальный сайт: npmjs.com.

Установка.

npm устанавливается вместе с Node.js.

Также вместе с Node.js устанавливается npx, позволяющий запускать исполняемые файлы без установки: npx create-react-app my-app.

Проверка установки:

node --version | -vnpm --version | -v

Обновление:

npm i -g npm@latest

Список доступных команд:

npm helpnpm help [command-name]

Инициализация проекта:

npm init// autonpm init --yes | -y

Установка зависимостей

npm install | i// проверка конкретной зависимостиnpm explore [package-name]// проверка всех зависимостейnpm doctor// очисткаnpm ci

Принудительная переустановка зависимостей:

npm i --force | -f

Установка только продакшн-пакетов:

npm i --only=production | --only=prod

Добавление зависимости:

npm i [package-name]npm i [package-name@version]// примерnpm i express

Добавление зависимости для разработки:

npm i --save-dev | -D [package-name]// примерnpm i -D nodemon

Обновление зависимости:

npm update | up [package-name]

Удаление зависимости:

// dependencynpm remove | rm | r [package-name]// devDependencynpm r -D [package-name]

Глобальная установка/обновление/удаление пакета:

npm i/up/r -g [package-name]// примерnpm i -g create-react-app// использованиеcreate-react-app my-app

Определение устаревших пакетов:

npm outdatednpm outdated [package-name]

Список установленных зависимостей:

npm list | ls// top levelnpm ls --depth=0 | --depth 0// global + top levelnpm ls -g --depth 0

Информация о пакете:

npm view | v [package-name]// примерnpm v reactnpm v react.description

Запуск скрипта/выполнение команды:

npm run [script]// пример// package.json: "scripts": { "dev": "nodemon server.js" }npm run dev// script start или node server.jsnpm startnpm stop

Удаление дублирующихся пакетов:

npm dedupe | ddp

Удаление посторонних пакетов:

npm prune

Обнаружение уязвимостей (угроз безопасности):

npm audit// jsonnpm audit --json// plain textnpm audit --parseable

Автоматическое исправление уязвимостей:

npm audit fix

yarn


yarn, как и npm, представляет собой пакетный менеджер, позволяющий устанавливать зависимости проекта.

Официальный сайт: yarnpkg.com.

Установка:

npm i -g yarn

Команда yarn dlx позволяет запускать исполняемые файлы без установки: yarn dlx create-react-app my-app. Для этого yarn необходимо обновить до второй версии: yarn set version berry.

Проверка установки:

yarn --version | -v

Обновление:

yarn set version latest

Список доступных команд:

yarn helpyarn help [command-name]

Инициализация проекта:

yarn init// autoyarn init --yes | -y// "private": true в package.jsonyarn init --private | -p// auto + privateyarn init -yp

Установка зависимостей:

yarn// илиyarn install// тихая установкаyarn install --silent | -s// проверкаyarn --check-files

Принудительная переустановка зависимостей:

yarn install --force

Установка только продакшн-пакетов:

yarn install --production | --prod

Добавление зависимости:

yarn add [package-name]yarn add [package-name@version]// примерyarn add express// тихая установкаyarn add --silent// илиyarn add -s

Добавление зависимости для разработки:

yarn add --dev | -D [package-name]// примерyarn add -D nodemon

Обновление зависимости:

yarn upgrade [package-name]

Удаление зависимости:

yarn remove [package-name]

Глобальная установка/обновление/удаление пакета:

yarn global add/upgrade/remove [package-name]// примерyarn global add create-react-app// использованиеcreate-react-app my-app

Список установленных зависимостей:

yarn list// top levelyarn list --depth=0 | --depth 0

Информация о пакете:

yarn info [package-name]// илиyarn why [package-name]// примерyarn info reactyarn info react descriptionyarn why webpack

Запуск скрипта/выполнение команды:

yarn [script]// илиyarn run [script]// пример// package.json: "scripts": { "dev": "nodemon server.js" }yarn dev

package.json


{  "name": "my-app",  "version": "1.0.0",  "description": "my awesome app",  "keywords": [    "amazing",    "awesome",    "best"  ],  "private": true,  "main": "server.js",  "license": "MIT",  "homepage": "https://my-website.com",  "repository": {    "type": "git",    "url": "https://github.com/user/repo.git"  },  "repository": "github:user/repo",  "author": {    "name": "My Name",    "email": "myemail@example.com",    "url": "https://my-website.com"  },  "author": "My Name <myemail@example.com> (http://personeltest.ru/aways/my-website.com)",  "contributers": [    {      "name": "Friend Name",      "email": "friendemail@example.com",      "url": "https://friend-website.com"    }  ],  "contributors": "Friend Name <friendemail.com> (http://personeltest.ru/aways/friend-website.com)",  "dependencies": {    "express": "^4.17.1"  },  "devDependencies": {    "nodemon": "^2.0.4"  },  "scripts": {    "start": "react-scripts start",    "dev": "nodemon server.js"  }}

  • name название проекта
  • version версия проекта (см. версионирование)
  • description описание проекта (зачем нужен пакет?)
  • keywords ключевые слова (облегчает поиск в реестре npm)
  • private установка значения в true предотвращает случайную публикацию пакета в реестре npm
  • main основная точка входа для функционирования проекта
  • repository ссылка на репозиторий (один из вариантов)
  • author автор проекта (один из вариантов)
  • contributors участники проекта (люди, внесшие вклад в проект)
  • dependencies зависимости проекта (пакеты, без которых приложение не будет работать)
  • devDependencies зависимости для разработки (пакеты, без которых приложение будет работать)
  • scripts команды (выполняемые сценарии, задачи), предназначенные для автоматизации, например, команда yarn dev запустит скрипт nodemon server.js

Полный список доступных полей файла package.json: npm-package.json

Файлы package-lock.json и yarn.lock содержат более полную информацию об установленных пакетах, чем package.json, например, конкретные версии пакетов вместо диапазона допустимых версий.

Версионирование


Каждый пакет имеет версию, состоящую из трех цифр (например, 1.0.0), где первая цифра мажорная версия (major), вторая минорная версия (minor), третья патчевая версия (патч, patch). Новые версии называются релизами.

Увеличение каждой из этих цифр согласно правилам семантического версионирования (semver) означает следующее:

  • major внесение несовместимых с предыдущей версией изменений
  • minor новая функциональность, совместимая с предыдущей версией
  • patch исправление ошибок, незначительные улучшения

Диапазоны версий или допустимые релизы определяются с помощью следующих операторов (компараторов):

  • * любая версия (аналогично пустой строке)
  • <1.0.0 любая версия, которая меньше 1.0.0
  • <=1.0.0 любая версия, которая меньше или равна 1.0.0
  • >1.0.0 любая версия, которая больше 1.0.0
  • >=1.0.0 любая версия, которая больше или равна 1.0.0
  • =1.0.0 только версия 1.0.0 (оператор "=" можно опустить)
  • >=1.0.0 <2.0.0 больше или равно 1.0.0 и меньше 2.0.0
  • 1.0.0-2.0.0 набор версий включительно
  • ^1.0.0 минорные и патчевые релизы (>=1.0.0 <2.0.0)
  • ~.1.0.0 только патчевые релизы (>=1.0.0 <1.1.0)

Подробные сведения о semver: node-semver.

Благодарю за внимание.
Подробнее..

Перевод Удобная платформа для подбора библиотек и фреймворков JavaScript openbase

15.10.2020 14:05:55 | Автор: admin
image

Что за зверь?


openbase.io

Находите и сравнивайте пакеты с открытым исходным кодом с отзывами пользователей, категоризацией и беспрецедентной информацией о популярности, надежности, активности пакетов и многом другом

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

Как я обычно выбираю себе библиотеку



В моем случае большинство из них для js, поэтому метод выбора библиотеки следующий.

  1. Поискать в npm
  2. Подобрать айтемы, подходящие по описанию, имеющие достаточное количество загрузок и получавшие обновления в последние несколько месяцев.
  3. Проверить доступность документации и readme на GitHub; иногда проверять наличие обновлений по ключевым вопросам.
    Кстати, я стараюсь не принимать решение только по наличию или отсутствию документов. Как правило, они могут находиться в процессе релиза или экстренных правок, о чем можно узнать на issue board, где разработчики и юзеры могут контактировать друг с другом.

После этого, я выбираю несколько вариантов, быстро проверяю их и решаю, какие буду применять.

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

Плюсы openspace Ревью


Для моих изысканий идеально подошел сервис openbase.io

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

В обзоре содержатся впечатления от использования и количество звезд. Сами обзоры тоже можно оценивать по степени полезности, что, в свою очередь, помогает понять ценность самого обзора.

Например, на React оставлено более 570 отзывов.

Общая информация


image

Ревью


image

Плюсы openbase Можно сразу найти туториалы


На боковой панели есть вкладка tutorial, в которой вам все подробно разъяснят, в том числе через ролики на YouTube.

image

image

Плюсы openbase информация об альтернативных пакетах


По вкладке Alternatives там же, на сайдбаре, можно найти библиотеки, схожие с теми, на которые вы наткнулись на данный момент. На картинке ниже результаты для React, особых откровений не ожидается, но в зависимости от результатов поиска можете наткнуться на интересные открытия. Думаю, у этой функции хороший потенциал, можно делать свои проекты более оригинальными за счет новых, небанальных пакетов.

image

Спасибо за прочтение!

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

Engine-version npm пакет, который позволит задать корректное окружение разработки

08.11.2020 22:05:43 | Автор: admin

Некоторые проекты зачастую требуют специфичные версии локально установленных программ. Это может быть как определенная версия node.js или npm (например, npm@7 с поддержкой workspaces), так и определенная база данных, менеджер пакетов и другие утилиты, которые нельзя установить из npm. Зачастую команды фиксирую версии в чатиках, readme или вики.

npm позволяет задекларировать в package.json файле необходимые версии node и npm, но никак не проверяет их. Чтобы исправить это и расширить список инструментов был написан небольшой npm пакет engine-version. Пакет работает очень просто: сначала он считывает описание необходимого софта из package.json, а затем смотрит установлена ли программа и совпадает ли установленная версия описанной. И если проверки прошли неудачно, отображается список ошибок.

Чтобы начать пользоваться пакетом нужно сначала установить пакет из npm:

npm install --save-dev engine-version

Описать список необходиммых инструментов в package.json (формат описания):

{  ...  "engines": {    "node": ">=16.0.0",    "npm": "~7.0.0",    "mysql": "*"  },  ...}

Добавить скрипт запускающий проверку в package.json, например, перед процессом сборки пакета:

{...  "scripts": {    "prebuild": "engine-version",    "build": "my_build_script"  },  ...}  

Дальше планируется сделать:

  • Сделать ошибки более информативными

  • Возможность конфигурирования при помощи аргументов (предупреждения вместо ошибок)

  • Добавить альтернативные виды проверки версий для популярных программ, не понимающих аргумент --version (сейчас проверка установленной версии выполняется вызовом программы с опцией --version в дочернем процессе)

Подробнее..

Новый график на Moiva.io с данными от StateOfJS

19.01.2021 14:16:27 | Автор: admin

Автор популярных ежегодных отчетов #StateOfJS и #StateOfCSS Sacha Greif (он же автор VulcanJS и Sidebar) обратился ко мне с идей включить данные отчета на Moiva.io.

Я ответил "Конечно!"

Что такое Moiva.io

Это проект цель которого максимально упростить сравнение жаваскрипт библиотек и показать тренды по ряду метрик.

О чем новый график

Я изучил последний отчет StateOfJS и решил что наиболее полезным будут данные о том какая часть разработчиков используют ту или иную библиотеку. StateOfJS охватывает только ограниченный набор библиотек, но зато самых популярных и трендовых библиотек. Для остальных я решил просто не отображать график.

Откуда данные

У меня уже и ранее была мысль добавить данные отчетов в проект и я намеревался вручную собрать все данные и захардкодить.

На мое счастье Sacha Greif подбросил идею использовать их апи. Выяснилось что у них есть открытое и очень толковое GraphQL api. О большем я и мечтать не мог. Дальнейшее - дело техники.

Заключение

Наиболее нагляден новый график на примере сравнения жаваскрипт фреймворков https://moiva.io/?compare=@angular/core+react+svelte+vue.
Любое мнение, фидбек и идеи очень приветствуются.

Подробнее..

Перевод Управление версиями Node.js и NPM с помощью NVM

08.02.2021 20:16:10 | Автор: admin
Наш прошлый перевод про новые функции 15-й версии Node.js был очень хорошо принят читателями Хабра, поэтому сегодня мы решили продолжить тему и рассказать, как настроить NVM с версией Node.js 15 и NPM 7.

Версия Node.js 15 была выпущена 20 октября 2020 года. Она поставляется с npm 7 и множеством новых функций. Вы уже успели опробовать новую версию?

Но подождите минутку! Node.js 15 и npm 7 содержат критические изменения. Не повредит ли тогда обновление существующим проектам?

Теоретически может повредить!



К счастью, у нас есть NVM (Node Version Manager), который избавит нас от этой опасности. Давайте детально рассмотрим данный инструмент, чтобы без проблем обновить версии node.js и npm.

Установка NVM


nvm управляет версиями node.js и npm. Он устанавливается для конкретного пользователя и может быть вызван отдельно для каждой оболочки. nvm работает с любой POSIX-совместимой оболочкой (sh, dash, ksh, zsh, bash), в том числе на платформах: unix, macOS и windows WSL.

nvm можно установить с помощью команд curl или wget:

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash$ wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash


Скрипт install.sh клонирует репозиторий nvm в ~/.nvm и пытается добавить исходные строки из приведенного ниже фрагмента в нужный файл профиля (~/.bash_profile, ~/.zshrc, ~/.profile или ~/.bashrc).

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

В ~/.bash_profile мы видим, что строки добавлены:

export NVM_DIR="/Users/fuje/.nvm"[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"  # This loads nvm[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Использование NVM


Итак, мы установили nvm. Теперь используем данную команду для установки последней версии node.js:

$ nvm install nodeDownloading and installing node v15.4.0...Downloading https://nodejs.org/dist/v15.4.0/node-v15.4.0-darwin-x64.tar.xz...######################################################################## 100.0%Computing checksum with shasum -a 256Checksums matched!Now using node v15.4.0 (npm v7.0.15)

В выходных данных из примера выше указано, что npm 7.0.15 используется вместе с node.js 15.4.0. Проверим:

$ node -vv15.4.0$ npm -v7.0.15

Также мы можем указать нужную версию для установки. Семантический формат версии определяется SemVer:

$ nvm install 10.14.0Downloading and installing node v10.14.0...Downloading https://nodejs.org/dist/v10.14.0/node-v10.14.0-darwin-x64.tar.xz...######################################################################## 100.0%Computing checksum with shasum -a 256Checksums matched!Now using node v10.14.0 (npm v6.4.1)

Если указанная версия уже была установлена, она не переустанавливается:

$ nvm install 10.14.0v10.14.0 is already installed.Now using node v10.14.0 (npm v6.4.1)

Мы можем вывести на экран все установленные версии:

$ nvm ls->     v10.14.0       v10.15.0       v10.16.0       v12.16.0        v13.9.0        v15.4.0         systemdefault -> 12.16.0 (-> v12.16.0)node -> stable (-> v15.4.0) (default)stable -> 15.4 (-> v15.4.0) (default)iojs -> N/A (default)unstable -> N/A (default)lts/* -> lts/fermium (-> N/A)lts/argon -> v4.9.1 (-> N/A)lts/boron -> v6.17.1 (-> N/A)lts/carbon -> v8.17.0 (-> N/A)lts/dubnium -> v10.23.0 (-> N/A)lts/erbium -> v12.20.0 (-> N/A)lts/fermium -> v14.15.1 (-> N/A)

В приведенных выше примерах вывода символ -> указывает, что текущая версия node.js 10.14.0. Стрелка также представляет значения для default (12.16.0), node (15.4.0) и stable (15.4.0).

nvm use заменяет текущую версию:

$ nvm use 12.16.0Now using node v12.16.0 (npm v6.14.8)$ nvm use 10.16.0Now using node v10.16.0 (npm v6.14.5)$ nvm use 13.9.0Now using node v13.9.0 (npm v6.13.7)$ nvm use defaultNow using node v12.16.0 (npm v6.14.8)$ nvm use nodeNow using node v15.4.0 (npm v7.0.15)$ nvm use stableNow using node v15.4.0 (npm v7.0.15)

Возможно, вы спросите, как так получилось, что v10.16.0 использует более позднюю версию npm, чем v13.9.0. Эту задачу можно решить с помощью следующих команд:

$ nvm use 10.16.0$ npm install -g npm@6.14.5

Данная команда позволяет получить последнюю поддерживаемую версию npm для текущей версии Node.js:

$ nvm install-latest-npm

nvm use устанавливает нужную версию только для текущей оболочки. Если вы измените оболочку, только что обновленная версия node.js будет потеряна.

Как сделать определенную версию Node.js постоянной?

Версия по умолчанию такая версия, которая распространяется на все оболочки.

Команда nvm alias позволяет установить версию по умолчанию.

$ nvm alias default 10.16.0


Для удобства можно создать файл .nvmrc, который принимает формат SemVer, node или default. После этого nvm use, nvm install, nvm exec, nvm run и nvm which будут использовать версию, указанную в файле .nvmrc, если в командной строке не указана другая.

$ cat .nvmrc15.4.0$ nvm useFound '/Users/fuje/.nvmrc' with version <15.4.0>Now using node v15.4.0 (npm v7.0.15)

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

$ nvm currentv15.4.0

ls-remote выводит на экран все доступные версии, но будьте готовы к очень длинному списку.

$ nvm ls-remote

Обратим внимание, что название версии в сокращенной форме значительно сокращает весь список.

$ nvm ls-remote 15        v15.0.0        v15.0.1        v15.1.0        v15.2.0        v15.2.1        v15.3.0->      v15.4.0

nvm which указывает путь к исполняемому файлу, где nvm был установлен. Мы установили такие версии node.js, как 10.14.0, 10.15.0 и 10.16.0. Вот результаты nvm which:

$ nvm which 10.14.0/Users/fuje/.nvm/versions/node/v10.14.0/bin/node$ nvm which 10.15.0/Users/fuje/.nvm/versions/node/v10.15.0/bin/node$ nvm which 10.16.0/Users/fuje/.nvm/versions/node/v10.16.0/bin/node$ nvm which 10.15/Users/fuje/.nvm/versions/node/v10.15.0/bin/node$ nvm which 10.12N/A: version "v10.12" is not yet installed.You need to run "nvm install 10.12" to install it before using it.$ nvm which 10/Users/fuje/.nvm/versions/node/v10.16.0/bin/node

Указанную версию Node.js можно использовать непосредственно для запуска приложений:

$ nvm run 10.15.0 app.js

Как вариант, данная команда запускает node app.js с переменной PATH, указывающей на версию 10.15.0.

$ nvm exec 10.15.0 node app.js

Если вам нужно больше nvm-команд, запустите команду help:

$ nvm --help


Обновление NVM


Мы можем использовать nvm для обновления node.js и npm. Но как обновить сам nvm?

Давайте попробуем!

Перед обновлением у нас установлен nvm 0.34.0.

Обновляем до версии 0.37.2.

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100 13527  100 13527    0     0  23046      0 --:--:-- --:--:-- --:--:-- 23083=> nvm is already installed in /Users/fuje/.nvm, trying to update using git=> => Compressing and cleaning up git repository=> nvm source string already in /Users/fuje/.bash_profile=> bash_completion source string already in /Users/fuje/.bash_profile=> Close and reopen your terminal to start using nvm or run the following to use it now:export NVM_DIR="$HOME/.nvm"[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Как указано в выводе, нам нужно закрыть и снова открыть терминал, чтобы использовать новую версию:

$ nvm --version0.37.2

По сравнению с версией 0.34.0, в версии 0.37.2 добавлена функция nvm set-colors для вывода на консоль.



По умолчанию nvm ls показывает следующие цвета:



Установим новые цвета:

$ nvm set-colors cgYmW

nvm ls отображает вывод с новыми цветами:



Заключение


nvm упрощает управление версиями node.js и npm. Теперь мы точно готовы перейти на node.js 15 и npm 7. Надеюсь, статья была полезной. Другие публикации автора можно найти здесь.
Подробнее..

Переиспользуемый компонент Svelte чтобы никому не было больно

10.02.2021 16:11:31 | Автор: admin

Переиспользуемый компонент Svelte: чтобы никому не было больно


Компонентные фреймворки независимо от названия никогда не покинут область только нишевого использования, если сообщество не будет создавать для них общедоступные компоненты, которые можно легко встроить в свой проект.


За последние года полтора для фреймворка Svelte уже создано множество различных компонентов, которые можно найти на NPM, GitHub или официальном списке. К сожалению, не все из них правильно "приготовлены" и порой их использование раздует размер бандла приложения сильнее, чем должно быть. А бывает, что такие пакеты просто невозможно использовать, потому что его автор не силён в подготовке пакетов и упустил какие-то важные моменты.


В этой статье я расскажу как сделать и опубликовать npm-пакет со Svelte-компонентом так, чтобы им смогли воспользоваться все желающие, не получив при этом головной боли от неожиданных проблем.


Создаем компонент


Для примера решим, что мы хотим представить миру компонент, который выводит часы с анимацией смены цифр. Посмотрим на его прототип в работе тут. Обратим внимание, что весь наш компонент состоит из трех файлов. Также у него имеется внешняя зависимость в виде библиотеки 'dayjs'.


Animated clock


Для начала понадобится пустая папка. Открыв её в терминале, инициализируем создание нового npm-пакета:


  npm init

Будет предложен ряд вопросов, первый из которых захочет узнать название нашего будущего пакета. По негласному правилу пакеты, содержащие Svelte-компоненты, принято называть с префиксом svelte-, так что назовем наш пакет, например, svelte-clock-demo. Это правило совершенно необязательно, но сильно упростит поиск компонента другими людьми. Также этому поспособствует и хороший набор ключевых слов, которые нужно перечислить через запятую в одном из следующих вопросов. Оставшиеся вопросы заполняйте на свое усмотрение, можно оставить значения по умолчанию нажатием клавиши Enter.


Теперь наша папка уже не пуста, в ней появился файл package.json к которому мы вернемся чуть позже, а пока создадим новую папку components рядом с этим файлом, куда поместим все файлы нашего компонента 'App.svelte','Sign.svelte' и 'flip.js'.


Список файлов


Также не забудем и про внешнюю зависимость, добавим её в пакет командой:


  npm install dayjs

Необходимо указать сборщикам Svelte-проектов, где в папке находится файл компонента, чтобы они могли импортировать его. Для этого откроем файл package.json и заменим строку "main":"index.js", на:


 ... "svelte":"components/App.svelte", ...

В принципе, этого уже достаточно, можно опубликовать пакет в таком виде и большинство разработчиков смогут использовать его в своем проекте. Но большинство это ещё не все...


Нужны модули


На текущий момент нашим пакетом смогут пользоваться только разработчики, которые делают свой Svelte-проект c настроенным сборщиком, который умеет работать с полем "svelte" в package.json. Но кроме этого, было бы неплохо если бы наш компонент могли использовать разработчики, которые делают проект на другом фреймворке или вообще без каких-либо фреймворков. Для них мы должны упаковать компонент в модули с которыми они смогут работать не настраивая плагинов для компиляции Svelte файлов в своих проектах.


ES6 модуль обычно используется при работе со сборщиками вроде Webpack или Rollup. Он должен содержать скомпилированный в JavaScript код нашего Svelte-компонента, а так же все CSS-стили, которые относятся к нашему компоненту. Однако, в модуль не должны быть включены внешние зависимости и различные вспомогательные функции из пакета svelte, например, переходы из svelte/transition или хранилища из svelte/store, а так же функции из svelte/internal. В противном случае, если пользователю понадобится использовать несколько различных пакетов, то в каждом из них будет своя копия фреймворка Svelte, что скажется на размере итогового бандла самым негативным образом.


IIFE модуль нужен для непосредственного использования в браузере в теге <script src="...">. Файлы таких модулей обычно кладут рядом с html-файлом, либо подключают с какого-либо CDN, вроде jsdelivr.com или unpkg.vom. Поскольку для таких модулей не существует никаких общепринятых механизмов управления зависимостями, то они должны быть самодостаточные и включать в себя всё, что необходимо для работы, включая все импорты из пакета svelte, а так же внешние зависимости такие как, dayjs из нашего примера.


Для сборки компонента в модули нам понадобится бандлер. Их существует великое множество Webpack, Rollup, Parcel и другие. Но лично я, в последнее время использую в своих проектах сборщик esbuild, он написан на Go, что позволяет ему собирать проекты невероятно быстро, также он достаточно прост в настройке и умеет оптимизировать бандл tree-шейкингом и минификацией. К нему в компанию нам понадобятся плагин esbuild-svelte и сам svelte. Установим все эти пакеты в dev-зависимости:


 npm install --save-dev esbuild esbuild-svelte svelte

Пакет svelte мы установили в dev-зависимости, поскольку он нам нужен для компиляции кода компонента в Javascript-код. В проектах, которые захотят использовать наш пакет вероятнее всего уже будет установлен svelte и все нужные импорты будут браться из него. Однако, возможна ситуация, когда этот пакет не будет установлен, а разработчик, используя наш ES6 модуль, получит сообщение об отсутствии зависимости. Чтобы избежать этой неприятной ситуации, добавим в package.json секцию с peer-зависимостью.


...,"peerDependencies": {  "svelte": "*"},...

Звездочка * говорит, что мы не требуем какой-то определенной версии Svelte, но при необходимости можно указать нужную версию.


Теперь при установке нашего пакета, если у пользователя не найдётся svelte в node_modules, будет предложено его установить. А NPM начиная с 7й версии сделает это автоматически.


Укажем бандлерам и CDN-сервисам где искать нужные им модули внутри нашего пакета. Для этого сразу под под полем "svelte":..., в package.json добавим ещё пару полей:


    ...    "module":"dist/clock.mjs",    "browser":"dist/clock.min.js",    ...

Конфигурация esbuild


В папке проекта создадим файл esbuild.js с таким содержанием:


const {build} = require(`esbuild`);const sveltePlugin = require(`esbuild-svelte`);// Берем содержимое package.json в виде объекта pkgconst pkg = require(`./package.json`);// Настраиваем плагин компиляции Svelte файловconst svelte = sveltePlugin({  compileOptions:{     // Все стили будут упакованы вместе с компонентом    css: true  }});// Собираем IIFE-модульbuild({  // Откуда и куда собирать модули узнаем в package.json  entryPoints: [pkg.svelte],  outfile: pkg.browser,  format: 'iife',  bundle: true,  minify: true,  sourcemap: true,  plugins: [svelte],  // Задаём имя глобальной переменной для доступа к модулю  globalName: 'svelteClock',})// Собираем ES-модульbuild({  entryPoints: [pkg.svelte],  outfile: pkg.module,  format: 'esm',  bundle: true,  minify: true,  sourcemap: true,  plugins: [svelte],  // Просим не включать в модуль зависимости из разделов  // dependencies и peerDependencies в файле package.json  external: [    ...Object.keys(pkg.dependencies),    ...Object.keys(pkg.peerDependencies),  ]})

Информацию про параметры конфигурации esbuild можно узнать в документации.

Добавим в package.json в раздел "scripts" скрипт для запуска esbuild:


  ...  "scripts": {    ...    "build":"node esbuild",    ...  }

Теперь можно запустить сборку модулей:


  npm run build

В результате в папке нашего проекта появится еще одна директория dist внутри которой будут два готовых файла модулей и файлы sourcemap, которые позволят легче отслеживать ошибки в компоненте, если таковые возникнут у пользователей.


Текущая структура файлов


Readme.md


Добавьте файл README.md в папку проекта, из него пользователи смогут узнать, как работать с вашим компонентом. Многие согласятся, что написать документацию, описывающую все нюансы работы с компонентом, задача, возможно, ещё более трудная, чем придумать сам этот компонент и правильно его собрать. Могу только порекомендовать указать назначение компонента, объяснить зачем он нужен людям. Показать примеры использования в других svelte-проектах, иных проектах и браузере. Если компонент конфигурируется при помощи экспортируемых свойств или отправляет какие-либо события, обязательно опишите как с этим работать. Писать документацию стоит на английском языке, поскольку вашим компонентом будут пользоваться люди со всего мира.


Публикация пакета


Вся необходимая подготовка проведена и мы готовы собрать и отправить наш пакет с компонентом на NPM. По умолчанию в пакет попадают все файлы и папки, которые содержатся в папке проекта, кроме директории node_modules и некоторых других специфичных файлов. Также не нужно включать в пакет файл esbuild.js, поскольку он нужен только на этапе сборки модулей. Для исключения определенных файлов папок из пакета воспользуемся файлом .npmignore, внутри которого на каждой строчке нужно указать какие файлы не должны попадать в пакет. В нашем случае это всего один файл esbuild.js. В более сложных проектах там стоит перечислить папки содержащие тесты и исходники, которые не нужны в собранном пакете.


Содержимое .npmignore


Каждый раз при публикации новой версии пакета мы должны не забыть выполнить скрипт npm run build, который поместит в папку dist актуальные версии модулей. Чтобы автоматизировать этот процесс добавьте в секцию "scripts" в package.json ещё один скрипт:


  ...  "scripts": {    ...    "prepublish":"npm run build",    ...  }

Если вы все сделали верно, то целиком файл pacakge.json должен выглядеть примерно так:


{  "name": "svelte-clock-demo",  "version": "1.0.0",  "description": "Animated clock component for Svelte",  "svelte": "components/App.svelte",  "module":"dist/clock.mjs",  "browser":"dist/clock.min.js",  "scripts": {    "build":"node esbuild",    "prepublish":"npm run build",    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "Vasya Pupkin",  "license": "ISC",  "dependencies": {    "dayjs": "^1.10.4"  },  "devDependencies": {    "esbuild": "^0.8.43",    "esbuild-svelte": "^0.4.1",    "svelte": "^3.32.2"  },  "peerDependencies": {    "svelte": "*"  }}

Наконец, всё готово и можно авторизоваться своей учетной записью в NPM и опубликовать пакет:


 npm login npm publish

Пакет будет доставлен в NPM и станет доступным для загрузки всеми желающими. А вам останется наблюдать за количеством установок вашего пакета и своевременно исправлять обнаруженные пользователями баги.


Используем переиспользуемый компонент


Итак компонент уже опубликован. Теперь можно его установить в папке какого-либо своего проекта:


  npm install --save-dev svelte-clock-demo

Если это проект Svelte-приложения, то импортируйте и используйте как обычный компонент:


<script>    import Clock from 'svelte-clock-demo';</script><Clock background="white" color="black" />

Если же это проект, в котором нет настроенного компилятора Svelte, то будет импортирован ES6 модуль, и компонент инициализируется следующим образом:


import Clock from 'svelte-clock-demo';new Clock({  // Указываем элемент DOM, куда будет отрисован компонент  target: document.getElementById('divForClock'),  // Передаём свойства компоненту  props:{    background: 'white',    color: 'black'  }})

Практически таким же образом можно использовать модуль прямо с CDN. Обычно после публикации пакета на NPM, через пару минут его версия появляется и в различных CDN сервисах, например jsdelivr.com.


<html>  <head>    <title>Страница с часами</title>    <!-- Подключаем компонент с CDN -->    <script src='https://cdn.jsdelivr.net/npm/svelte-clock-demo'></script>  </head>  <body>    <div id="divForClock"></div>  </body>  <script>    // Имя глобальной переменной было задано в конфигурации esbuild    new svelteClock.default({      // Указываем элемент DOM, куда будет отрисован компонент      target: document.getElementById('divForClock'),      // Передаём свойства компоненту      props:{        background: 'white',        color: 'black'      }    })  </script></html>

Библиотека компонентов


Иногда в один пакет нужно поместить больше, чем один-единственный компонент. Яркий пример, различные библиотеки UI элементов, состоящие из компонентов вроде Input,Button, Checkbox и т.п.


В нашем пакете тоже можно предоставлять пользователю не только готовый компонент часов из App.svelte, но и компонент отдельной цифры Sign.svelte. Тогда разработчики смогут сделать на его основе, например, таймер обратного отсчета или какой-либо счетчик.


Для создания библиотеки компонентов, добавим в папку components файл index.js внутри которого сделаем ре-экспорт всех компонентов, которые мы хотим сделать доступными в нашем пакете.


export {default as Clock} from './App.svelte';export {default as Sign} from './Sign.svelte';

Затем нужно поменять значение поля "svelte" в package.json, указав там путь до файла index.js.


  ...  "svelte":"components/index.js",  ...

После публикации пакета, можно импортировать только нужные компоненты:


import {Sign} from 'svelte-clock-demo';

Заключение


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


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

Подробнее..

Перевод Зависимости JavaScript Все, что вы когда-либо хотели знать, но боялись спросить

02.03.2021 16:05:07 | Автор: admin

Ваше подробное руководство по пяти типам зависимости

Привет, хабровчане. Для будущих учащихся на курсе "JavaScript Developer. Professional" подготовили перевод материала.

Также приглашаем всех желающих на открытый вебинар по теме
Vue 3 возможности новой версии одного из самых популярных фронтенд фреймворков. На занятии участники вместе с экспертом:
рассмотрят ключевые отличия в синтаксисе vue2 от vue3;
разберут, как работать с vue-router и VueX в новой версии фреймворка;
cоздадут проект на Vue 3 с нуля с помощью Vue-cli.


Независимо от того, являетесь ли Вы back-end разработчиком, работающим с Node.js, или front-end разработчиком, использующим Node.js только в качестве инструмента для пакетирования и комплектации, Вы наверняка наткнулись на систему зависимостей.

Но почему их 5 типов (да, это не опечатка, есть 5 типов зависимостей), и для какого случая они используются? На эти вопросы мы ответим сегодня, так что сядьте поудобнее и расслабьтесь, потому что это будет интересно.

. . .

Normal (runtime) dependencies (cтандартные (во время выполнения программы) зависимости)

Давайте начнем с простого, хорошо?

Стандартные зависимости это те, которые вы видите в списке "dependencies" в вашем файле package.json. В большинстве случаев они указывают только имя (для своего ключа) и версию (значение), а затем NPM (node package manager или любой другой менеджер пакетов) позаботится об их захвате из глобального регистра (на npmjs.org).

Однако, это еще не все. Вместо точного номера версии вашей зависимости вы можете указать:

  • Примерный вариант. Вы можете работать с обычными числовыми операторами сравнения, указывая версии больше одного определенного числа (т.е. >1.2.0 ), любой версии ниже или равной другому числу (т.е. <=1.2.0 ), а также можете обыгрывать любую из их комбинаций (т.е. >= , > , <) . Вы также можете указать эквивалентную версию с помощью оператора ~ перед номером версии (т.е. "lodash":"~1.2.2, который загрузит что угодно между 1.2.2 и 1.3.0 или, другими словами, только патчи). И мы также можем указать "совместимую" версию с другим номером, который использует semver для понимания совместимости (т.е. "lodash": "^1.2.0", которая не загрузит ничего, из того что включает в себя изменение с нарушением или отсутствием каких-либо функций).

  • URL-АДРЕС. Правильно, вы даже можете обойтись без версии и напрямую ссылаться на определенный URL, по сути загрузив этот модуль откуда-нибудь еще (например, с Github или напрямую скачав tarball-файл).

  • Локальный файл. Да, вы даже можете непосредственно обращаться к одному из ваших локальных файлов. Это очень удобно, если вы разрабатываете модуль и хотите протестировать его на проекте, прежде чем выпускать его на NPM. С помощью опции "Локальный файл" вы можете сделать ссылку на папку вашего локального модуля. Вы можете использовать как полный, так и частичный пути, при условии, что вы используете префикс-функцию с помощью file:// .

Когда ты будешь использовать стандартные зависимости?

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

Это может показаться простым, но если, например, ваш проект на самом деле является модулем, то вам нужно более внимательно разобраться в том, что это значит. Если ваш модуль предназначен для использования с другими проектами, такими как React или Babel, ваши зависимости не должны включать их, так как они, как предполагается, уже присутствуют, тем не менее, они все равно нужны (мы это увидим позже, где они появятся через секунду).

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

. . .

Peer dependencies (Равные зависимости)

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

Подождите, зависимость, которая вам не требуется? Я знаю, это звучит странно, но подумайте о плагине Babel, он нуждается в Babel в том смысле, что если его там нет, то он не сможет работать, однако, он может и не требовать Babel явно для чего-либо.

Теперь, потенциально, вы можете добавить Babel в список стандартных зависимостей, однако, при установке плагина, он также попытается установить Babel. И если вы используете, например, NPM, он установит его как локальную зависимость вашего плагина, что означает он будет дублировать уже существующую установленную Babel для хост-проекта, находящегося в папке плагина.

Когда ты будешь использовать Peer dependencies?

Вы можете прочитать больше о Peer dependencies здесь, однако, поймите, что вы будете использовать их только тогда, когда нужно указать на недостаточно прочную связь с другим проектом.

Другими словами:

  • Когда она вам нужна, но нет необходимости употреблять ее сразу и однозначно. Тогда это peer dependency.

  • Когда она вам нужна, но она уже должна быть установлена другим проектом. Тогда это peer dependency.

Примерами того, когда вы хотите использовать peerDependencies, являются:

  • Babel плагины. Ты хочешь декларировать такие же вещи, как и сам Babel, в качестве равной зависимости (peer dependency).

  • Express middleware packages (Экспресс-пакеты для промежуточной обработки): Это всего лишь один пример модуля NPM, который требует использования peer dependencies. Вы хотите определить приложение Express как зависимость, но не жесткую, в противном случае каждое промежуточное ПО (middleware) будет каждый раз инсталлировать всю структуру заново.

  • Если вы собираете Micro Frontend (Микрофронтенд), пытаясь решить, какие зависимости являются внешними (чтобы они не были связаны), а какие нет. Peer dependencies могут быть хорошим решением для этого.

  • Bit components. Если вы создаете и публикуете front-end компоненты, например, когда вы совместно используете React-компоненты на Bit (Github), вам нужно назначить react библиотеку как peer dependency. Это позволит удостовериться, что нужная библиотека доступна в хостинговом проекте без установки ее в качестве прямой зависимости от него.

Например, взгляните на этот React компонент, который я опубликовал некоторое время назад: это простая кнопка, которую можно выбрать (вы нажимаете на нее и она остается выбранной до тех пор, пока не будет нажата снова).

Снимок экрана component, как видно на Bits component hub.

Если вы установите его, вы получите полный код, который содержит файл package.json, в котором перечислены все peer dependencies:

Обратите внимание, что пакет не имеет прямой зависимости, несмотря на то, что для работы ему необходима React-библиотека.

Это также помогает сохранить размер нашего компонента как можно меньше (1KB) ничего лишнего не добавляется.

. . .

Dev Dependencies (Dev зависимости)

Имя говорит обо всем, не так ли? Я имею в виду, что так и должно быть, но когда имена действительно помогают нам, разработчикам, в нашей повседневной работе? Я точно знаю.

Зависимости процесса разработки (Development dependencies) предназначены для того, чтобы содержать только те модули, которые вы используете на этапе разработки вашего проекта.

Да, но есть и другие, например, инструменты для подшивки (linting tools), документация и тому подобное.

Дело в том, что если вы можете запустить проект без использования этого набора инструментов, тогда это dev dependency.

Принцип их работы заключается в том, что зависимости разработки устанавливаются только при запуске npm install или npm link из корневой папки вашего проекта.

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

Когда ты будешь использовать dev dependencies?

Любая зависимость, которая не требуется для производственного процесса, скорее всего, будет считаться dev dependencies (зависимости развития).

Дело в том, что это отлично работает для всех модулей, кроме вашего хост-проекта.

Все установленные внутри хост-проекта зависимости будут пропускать dev-зависимости модулей во время инсталляции, но всякий раз, когда вы повторно запускаете npm install в хост-проекте, он будет устанавливать заново все пропущенные ранее dev-зависимости модулей.

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

. . .

Связанные зависимости (Bundled Dependencies)

Они предназначены для тех случаев, когда вы упаковываете свой проект в один файл. Это делается с помощью команды npm pack, которая превращает вашу папку в тарбол (tarball-файл).

Теперь, если вы сделаете это и добавите имена некоторых из ваших зависимостей в массив под названием bundledDependencies (забавный факт: если вы используете bundleDependencies, это тоже будет работать), то тарбол также будет содержать эти зависимости внутри этого массива.

{...   "dependencies": {    "lodash": "1.0.2",    "request": "4.0.1"  },  "bundledDependencies": ["lodash"]...}

Посмотрите на эту часть файла package.json, с такой установкой, при запуске команды npm pack вы получите файл tarball, также содержащий пакет lodash внутри.

Это очень удобно при распространении ваших пакетов в однофайловом формате (помните, что вы также можете указать их URL или локальный путь как стандартную зависимость).

. . .

Дополнительные зависимости (Optional dependencies)

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

Обычно, когда вы запускаете npm install и процесс не может установить обычную зависимость по какой-либо причине (будь то из-за сбоя в сети, не может найти файл, версию или что-нибудь ещё), то он выдаст ошибку и отменит процесс установки.

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

let foo = null;try {  foo = require("foo-dep");} catch (e) {  foo = require("./local-polyfill")}//... use foo from here on out

Когда вы будете использовать дополнительные зависимости (optional dependencies)?

Очевидный пример использования этого типа зависимостей когда вы используете ненадежный источник. Я спрашиваю, зачем это делать? Хороший вопрос. Понятия не имею, но вы определенно можете это сделать, единственная проблема в том, что вам нужно убедиться, что ваш код может с этим справиться, если отсутствуют зависимости.

Но другой интересный вариант использования, это установка действительно необязательных зависимостей (optional dependencies). Я имею в виду, что иногда у вас могут быть специфические для системы зависимости, для таких вещей, как совместимость с CI(Continuous Integration)-платформой. В таких сценариях, когда используете платформу, вы захотите установить эти зависимости, в другом случае, проигнорируете.

Для таких ситуаций можно использовать обычную npm install, когда вы используете полный набор зависимостей, а затем использовать npm install --no-optional, когда вы хотите избежать их. Таким образом, вы пропустите эти опции и сосредоточитесь только на обязательных зависимостях.

. . .

Каждый, кто когда-либо пользовался NPM в тот или иной момент, слышал о стандартных и dev-зависимостях. Остальные 3, однако, можно считать менее популярными собратьями.

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

Знали ли вы, что у вас так много вариантов? Оставьте комментарий ниже, если вы использовали некоторые из менее распространенных и расскажите нам, как вы их использовали!


Узнать подробнее о курсе "JavaScript Developer. Professional".

Смотреть открытый вебинар по теме Vue 3 возможности новой версии одного из самых популярных фронтенд фреймворков.

Подробнее..

Exports в package.json

09.03.2021 22:22:04 | Автор: admin

Привет. Я работаю в команде, занимающейся улучшением пользовательского опыта
при работе с деньгами. Front-end мы поставляем npm-пакетами.

В какой-то момент я столкнулся с проблемами, которые привели меня к использованию
поля exports в package.json


Проблема #1


Пакеты могут экспортировать функции с одинаковыми названиями, но делающие разные вещи. Для примера возьмём 2 стейт-менеджера: Reatom и Effector.

Они экспортируют функцию createStore. Если попытаться экспортитировать их из одного пакета (назовём его vendors), то получится следующая картина:


// @some/vendors/index.tsexport { createStore } from '@reatom/core';export { createStore } from 'effector';

Налицо конфликт имён. Такой код просто не будет работать.


Этого можно избежать за счёт as:


// @some/vendors/index.tsexport { createStore as reatomCreateStore } from '@reatom/core';export { createStore as effectorCreateStore } from 'effector';

Выглядит, прямо скажем, паршиво. По-хорошему, каждый реэкспорт надо писать через as для поддержания консистентности. Как по мне, это ухудшает DX.

Я начал искать решение, которое позволит избежать и конфликта имён и необходимости писать as. Как бы оно могло выглядеть Например, вот так:


// @some/vendors/reatom.tsexport { createStore } from 'reatom';

// @some/vendors/effector.tsexport { createStore } from 'effector';

В двух разных файлах мы пишем обычные экспорты и импортируем нужную реализацию createStore:


// someFile.tsimport { createStore } from 'vendors/effector';

Проблема #2


Пакет vendors, скорее всего, содержит не только стейт-менеджер, а ещё какую-то библиотеку. Например, Runtypes.
Если не использовать exports, то импорт будет выглядеть вот так:


// someFile.tsimport { createStore, Dictionary, createEvent, Record } from 'vendors';

Получается какая-то мешанина. На мой взгляд, приятнее было бы читать что-то в стиле:


// someFile.tsimport { createStore, createEvent } from 'vendors/effector';import { Dictionary, Record } from 'vendors/runtypes';

А ещё хорошо бы скрыть имена библиотек. Это может быть полезно при последующих рефакторингах.


// someFile.tsimport { createStore, createEvent } from 'vendors/state';import { Dictionary, Record } from 'vendors/contract';

Решение


Чтобы добиться таких импортов, необходимо обратиться к полю exports в package.json


// package.json"exports": {  "./contract": "./build/contract.js",  "./state": "./build/state.js",  "./package.json": "./package.json"}

По сути, мы просто говорим сборщику как резолвить импорты. Если вы пишете на TypeScript, то это ещё не всё.

В package.json есть поле types, которое позволяет указать, где находятся типы пакета. В значении у него строка. Не получится указать типы и для contract, и для state. Так что же делать?

Тут на помощь приходит typesVersions в package.json


// package.json"typesVersions": {  "*": {    "contract": ["build/contract.d.ts"],    "state": ["build/state.d.ts"]  }}

Мы делаем то же самое, что и в exports, но для d.ts файлов, получая рабочие типы.


Заключение


Использование exports не ограничивается проблемой создания пакета vendors. Его можно использовать для улучшения DX.

Например, в Effector базовый импорт выглядит вот так:


import { createEvent } from 'effector';

А для поддержки старых браузеров вот так:


import { createEvent } from 'effector/compat';

Какие ещё проблемы решает exports можно ознакомиться тут.
Посмотреть на репозиторий с примером здесь.


Спасибо!

Подробнее..

Из Vue 2 на Vue 3 Migration Helper

09.06.2021 20:16:20 | Автор: admin

Предистория

Была у меня курсовая по веб-разработке, делать очередной интернет-магазин как-то не хотелось, и решил я написать помощник миграции из Vue 2 (options-api) в Vue 3 (composition-api) с авторазделением на композиции с помощью алгоритма Косарайю по поиску областей сильной связности

Для тех, кто не в теме, поясню, так выглядит код с options-api:

export default {  data () {    return {      foo: 0,      bar: 'hello',    }  },  watch: {    ...  },  methods: {    log(v) {      console.log(v);    },  },  mounted () {    this.log('Hello');  }}

и примерно так с composition-api:

export default {  setup (props) {    const foo = reactive(0);    const bar = reactive('hello');    watch(...);    const log = (v) => { console.log(v); };    onMounted(() => { log('hello'); });    return {      foo,      bar,      log,    };  }}

Автоматическое разделение на композиции

Дабы не отходить от самой идеи композиций, помимо трансляции кода под новый синтаксис composition-api, было принято решение добавить и возможность разделения монолитного компонента на самостоятельные композиции, и их последующее переиспользование в главном компоненте. Как же это сделать?

Сначала зададимся вопросом, что же такое композиции? Для себя я ответил так:

Композиции это самодостаточная группа блоков кода, отвечающих за один функционал, зависящих только друг от друга. Зависимости тут самое главное!

Блоками кода в нашем случае будем считать: свойства data, методы, вотчеры, хуки, и все то, из чего строится компонент Vue.

Теперь определимся на счёт зависимостей блоков кода между собой. С этим во Vue достаточно просто:

  • Если computed, method, hook, provide свойство внутри себя использует другие свойства, то оно от них и зависит

  • Если на свойство навешен вотчер, то вотчер зависит от наблюдаемого им свойства

  • и так далее :)

data: () => ({  array: ['Hello', 'World'], // block 1}),watch: {  array() { // block 2 (watch handler) depends on block 1    console.log('array changed');  },},computed: {  arrayCount() { // block 3    return this.array.length; // block 3 depends on block 1  },},methods: {  arrayToString() { // block 4    return this.array.join(' '); // block 4 depends on block 1  }},

Допустим, мы смогли пройтись по коду и выделить все-все зависимости свойств между собой. Как всё это делить на композиции?

А теперь абстрагируемся от Vue, проблемы миграции, синтаксиса и т.д. Оставим только сами свойства и их зависимости друг с другом.

Выделим из этого ориентированный граф, где вершинами будут свойства, а ребрами - зависимости между свойствами. А теперь самое интересное!

Алгоритм Косарайю

Алгоритм поискаобластей сильной связностив ориентированном графе. Заключается он в двух проходах в глубину по исходному и транспонированному графам и небольшой магии.

Никогда бы не подумал, что простое переписывание реализации из C на TS может быть таким проблемным :)

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

Поиск зависимостей

Примечание: во всех функциях компонента в options-api свойства доступны через this

Здесь немного грусти, поскольку искать зависимости в .js приходится так:

const splitter = /this.[0-9a-zA-Z]{0,}/const splitterThis = 'this.'export const findDepsByString = (  vueExpression: string,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  return vueExpression    .match(splitter)    ?.map((match) => match.split(splitterThis)[1])    .filter((value) => instanceDeps[value])    .map((value) => value)

Да, просто проходясь регуляркой по строкому представлению функции в поисках всего, что идет после this. :(

Более продвинутый вариант, но такой же костыльный:

export const findDeps = (  vueExpression: Noop,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  const target = {}  const proxy = new Proxy(target, {  // прокси, который записывает в объект вызываемые им свойства    get(target: any, name) {      target[name] = 'get'      return true    },    set(target: any, name) {      target[name] = 'set'      return true    }  })  try {    vueExpression.bind(proxy)() // вызываем функцию в скоупе прокси    return Object.keys(target) || [] // все свойства которые вызвались при this.  } catch (e) { // при ошибке возвращаемся к первому способу    return findDepsByString(vueExpression.toString(), instanceDeps) || []  }}

При использовании прокси вышло несколько проблем:

  • не работает с анонимными функциями

  • при использовании вызывается сама функция а если вы там пентагон взламываете?

Создание файлов и кода

Вспомним зачем мы тут собрались: миграция.

Используя все вышеописанное, получив разбитые по полочкам свойства, нужно составить новый код в синтаксисе composition-api, то есть собрать строки, которые в конечном счете будут являться содержимыми файлов в проекте.

Для этого надо уметь представлять экземпляры объектов, строк, массивов и всего остального в их естественном, кодовом, виде. Вот эта функция:

const toString = (item: any): string => {  if (Array.isArray(item)) {    // array    const builder: string[] = []    item.forEach((_) => {      builder.push(toString(_)) // wow, it's recursion!    })    return `[${builder.join(',')}]`  }  if (typeof item === 'object' && item !== null) {    // object    const builder: string[] = []    Object.keys(item).forEach((name) => {      builder.push(`${name}: ${toString(item[name])}`) // wow, it's recursion!    })    return `{${builder.join(',')}}`  }  if (typeof item === 'string') {    // string    return `'${item}'`  }  return item // number, float, boolean}// Exampleconsole.log(toString([{ foo: { bar: 'hello', baz: 'hello', }}, 1]);// [{foo:{bar: 'hello',baz: 'hello'}},1]  т.е. то же самое, что и в коде

Про остальной говнокод я тактично промолчу :)

Итоговые строки мы записываем в новые файлы через простой fs.writeFile() в ноде и получаем результат

Пример работы

Собрав всё это в пакет, протестировав и опубликовав, можно наконец увидеть результат работы.

Ставим пакет vue2-to-3 глобально (иначе не будет работать через консоль) и проверяем!

Пример HelloWorld.js:

export default {  name: 'HelloWorld',  data: () => ({    some: 0,    another: 0,    foo: ['potato'],  }),  methods: {    somePlus() {      this.some++;    },    anotherPlus() {      this.another++;    },  },};

Пишем в консоли: migrate ./HelloWorld.js и получаем на выход 3 файла:

// CompositionSome.jsimport { reactive } from 'vue';export const CompositionSome = () => {  const some = reactive(0);  const somePlus = () => { some++ };  return {    some,    somePlus,  };};// CompositionAnother.jsimport { reactive } from 'vue';export const CompositionAnother = () => {  const another = reactive(0);  const anotherPlus = () => { another++ };  return {    another,    anotherPlus,  };};// HelloWorld.jsimport { reactive } from 'vue';import { CompositionSome } from './CompositionSome.js'import { CompositionAnother } from './CompositionAnother.js'export default {  name: 'HelloWorld',  setup() {    const _CompositionSome = CompositionSome();    const _CompositionAnother = CompositionAnother();    const foo = reactive(['potato']);    return {      foo,      some: _CompositionSome.some,      somePlus: _CompositionSome.somePlus,      another: _CompositionAnother.another,      anotherPlus: _CompositionAnother.anotherPlus,    };  },};

Итого

На данный момент все это доступно и работает, но ещё есть некоторые баги со строковым представлением не анонимных функций и путями (в некоторых случаях фатально для linux систем)

В планах запилить миграцию для single-file-components и .ts файлов (сейчас работает только для .js)

Спасибо за внимание!

npm, git

Подробнее..

Настраиваем экспорт IPFIX на VMware vSphere Distributed Switch (VDS) и последующий мониторинг трафика в Solarwinds

26.06.2020 08:09:31 | Автор: admin
Привет, Хабр! В начале июля Solarwinds анонсировал релиз новой версии платформы Orion Solarwinds 2020.2. Одно из нововведений в модуле Network Traffic Analyzer (NTA) поддержка распознавания IPFIX-трафика от VMware VDS.



Анализ трафика в среде виртуального коммутатора важен для понимания распределения нагрузки на виртуальную инфраструктуру. Анализируя трафик, можно также обнаружить миграцию виртуальных машин. В этой статье мы расскажем о настройках экспорта IPFIX на стороне виртуального коммутатора VMware и о возможностях Solarwinds по работе с ним. А в конце статьи будет ссылка на онлайн-демо Solarwinds (доступ без регистрации и это не фигура речи). Подробности под катом.

Для корректного распознавания трафика от VDS, сначала нужно настроить подключение через интерфейс vCenter, а уже потом анализ трафика и отображение точек обмена трафиком, получаемого от гипервизоров. При желании, коммутатор может быть настроен на получение всех записей IPFIX с одного IP-адреса, привязанного к VDS, но, в большинстве случаев, более информативно видеть данные, извлекаемые из трафика, полученного от каждого гипервизора. Трафик, который приходит, будет представлять соединения от или к виртуальным машинам, расположенных на гипервизорах.

Еще один доступный вариант конфигурации экспорт только внутренних потоков данных. Эта опция исключает потоки, которые обрабатываются на внешнем физическом коммутаторе, и предотвращает дублирование записей трафика для соединений от и к VDS. Но полезнее отключить эту опцию и наблюдать за всеми потоками, которые видны в VDS.

Настройка трафика от VDS


Начнём с добавления экземпляра vCenter в Solarwinds. После этого NTA будет обладать информацией о конфигурации платформы виртуализации.

Перейдите в меню Manage Nodes, далее Settings и выберите Add Node. После этого нужно ввести IP-адрес или полное доменное имя экземпляра vCenter и выбрать VMware, Hyper-V, or Nutanix entities в качестве метода опроса.

image

Перейдите в диалоговое окно добавления узла, добавьте учетные данные экземпляра vCenter и протестируйте их для завершения настройки.

image

В течении некоторого времени будет выполняться начальный опрос экземпляра vCenter, обычно 10-20 минут. Нужно дождаться завершения, а уже потом включить экспорт IPFIX на VDS.

После настройки мониторинга vCenter и получения инвентарных данных по конфигурации платформы виртуализации, включим на коммутаторе экспорт записей IPFIX. Самый быстрый способ сделать это через клиент vSphere. Перейдем на вкладку Networking, выберем VDS и на вкладке Configure найдём текущие настройки для NetFlow. VMware использует термин NetFlow для обозначения экспорта потока, но фактический протокол, который используется IPFIX.

image

Чтобы включить экспорт потока, выберите Settings в меню Actions вверху и перейдите к Edit NetFlow.

image

В этом диалоговом окне введите IP-адрес коллектора, который по совместительству является экземпляром Orion. По умолчанию обычно используется порт 2055. Рекомендуем оставить поле Switch IP Address пустым, что приведет к получению потоковых записей, получаемых именно от гипервизоров. Это даст гибкость при дальнейшей фильтрации потока данных от гипервизоров.

Оставьте поле Process internal flows only отключенным, что позволит видеть все коммуникации: как внутренние, так и внешние.

Как только включите экспорт потока для VDS, нужно будет включить его также и для распределенных порт-групп, от которых хотите получать данные. Самый простой способ сделать это щелкнуть правой кнопкой мыши на панели навигации VDS и выбрать Distributed Port Group, а затем Manage Distributed Port Groups.

image

image

Откроется диалоговое окно, в котором нужно установить флажок Monitoring и нажать Next.

На следующем шаге можете выбрать определённые или все группы портов.

image

На следующем шаге переключите NetFlow на Enabled.

image

Когда на VDS и распределенных порт-группах включен экспорт потока, вы увидите, что записи потоков для гипервизоров начинают поступать в экземпляр NTA.

image

Гипервизоры можно увидеть в списке источников данных потока на странице Manage Flow Sources в NTA. Переключитесь на Nodes.

image

Результаты настройки вы можете увидеть на демо-стенде. Обратите внимание на возможность провалиться на уровень узла, протокола взаимодействия и т.д.



Интеграция с другими модулями Solarwinds в одном интерфейсе, позволяет проводить расследования в различных разрезах: посмотреть какие пользователи входили на виртуальную машину, производительность сервера (посмотреть демо), и приложений на нём, посмотреть связанные сетевые устройства и много чего ещё. Например, если в вашей сетевой инфраструктуре используется протокол NBAR2, Solarwinds NTA может успешно распознавать трафик от Zoom, Teams или Webex.

Основная цель статьи показать простоту настройки мониторинга в Solarwinds и полноту собираемых данных. В Solarwinds есть шанс увидеть полную картину происходящего. Если хотите презентацию решения или проверить всё у себя оставьте заявку в форме обратной связи или позвоните.

На Хабре у нас также есть статья о бесплатных решениях Solarwinds.

Подписывайтесь на нашу группу в Фейсбуке.
Подробнее..

Перевод Простое ускорение Java с помощью Quarkus и JHipster

01.06.2021 20:15:10 | Автор: admin

К старту курса о разработке на Java делимся переводом вводной статьи о Quarkus "родной" для Kubernetes Java-платформе для создания высокопроизводительных веб-, бессерверных (serverless) и нативных приложений (оптимизированных для используемых микропроцессоров). В ней используются предварительная компиляция AOT и агрессивная оптимизация, например сканирование путей к классам, перезагрузка конфигурации и предварительная конфигурация самозагрузки приложения в процессе сборки. Результатом становится впечатляющая скорость загрузки. Другими словами, приложения, созданные с Quarkus, запускаются не просто быстро, а очень быстро!


Так же как для платформ Spring и Micronaut, в Quarkus можно использовать преимущество GraalVM для преобразования JVM-приложений в нативные исполняемые файлы, что ещё больше повышает их быстродействие.

Такой прирост производительности позволяет этой Java-платформе конкурировать с бессерверными, облачными и созданными на основе Kubernetes средами, создавая приложения Supersonic Subatomic Java. В Quarkus используются стандарты Java (такие, как MicroProfile, JAX-RS), а также лучшие из существующих Java-библиотек (например, Hibernate и Vert.x). Есть даже поддержка аннотаций Spring.

Quarkus + JHipster = простое ускорение Java

История технологий разработки наглядно демонстрирует, что совместить различные компоненты бывает очень непросто. Разработчики любят, когда им предлагается что-то авторитетно-одобренное, и как раз здесь на сцену выходит JHipster.

JHipster это сопровождаемая сообществом разработчиков полнофункциональная платформа разработки, позволяющая создавать, развивать и развёртывать веб-приложения и ориентированные на микросервисы архитектуры. Стандартной серверной платформой в JHipster является Spring Boot, но постоянно появляются всё новые и новые возможности. Одним из таких вариантов стала blueprint-схема JHipster Quarkus.

Помимо прочего, одной из ключевых возможностей, определяющих успех JHipster, стала его расширяемость с помощью blueprint-схем. Blueprint-схема действует как плагин JHipster и позволяет вам переопределять стандартное поведение, настраивая его на свой вкус.

Например, blueprint-схема Kotlin с JHipster является популярным дополнением, которое любят использовать разработчики. Использование blueprint-схемам многократно расширяет границы возможностей. Вот почему в JHipster есть даже реализации без Java (такие, как Node + NestJS и .NET Core). Эта публикация познакомит вас с основными шагами по использованию JHipster, blueprint-схемой Quarkus, а также протоколом OAuth для создания оптимизированного для работы на конкретных микропроцессорах и безопасного полнофункционального приложения.

Создание Java-приложения с Quarkus

Прежде чем приступить к работе, необходимо установить несколько инструментов.

Предварительные требования:

Установите JHipster и его blueprint-схему Quarkus, используя npm:

# Install JHipster globallynpm install -g generator-jhipster@6.10.5# Install the JHipster Quarkus blueprintnpm install -g generator-jhipster-quarkus@1.1.1

Команда jhipster-quarkus сокращение для jhipster --blueprints quarkus. Посмотреть все варианты её синтаксиса можно с помощью команды --help.

$ jhipster-quarkus --help

Создание приложения в среде JHipster Quarkus

mkdir okta-jhipster-quarkus-example && cd okta-jhipster-quarkus-example# oh-my-zsh users: take okta-jhipster-quarkus-example

Чтобы запустить мастер создания приложений, можно выполнить команду jhipster-quarkus:

jhipster-quarkus

В этой вводной статье основным вопросом будет выполнение аутентификации.

Среда JHipster Quarkus позволяет использовать JWT (с возможностью управления пользователями в базе данных приложений) или аутентификацию OAuth 2.0/OIDC с применением поставщиков идентификационной информации, таких как Keycloak и Okta. OIDC расшифровывается как OpenID Connect, этот протокол представляет собой тонкий слой поверх протокола аутентификации OAuth 2.0. Его основная задача обеспечить аутентификацию и идентификацию пользователя.

Ниже представлен пример ответов на вопросы мастера создания приложений.

После того как вы ответите на все эти вопросы, JHipster создаст код вашего приложения и выполнит команду npm install.

{  "generator-jhipster": {    "promptValues": {      "packageName": "com.mycompany.myapp",      "nativeLanguage": "en"    },    "jhipsterVersion": "6.10.5",    "applicationType": "monolith",    "baseName": "jhipster",    "packageName": "com.mycompany.myapp",    "packageFolder": "com/mycompany/myapp",    "serverPort": "8080",    "authenticationType": "oauth2",    "cacheProvider": "no",    "enableHibernateCache": true,    "websocket": false,    "databaseType": "sql",    "devDatabaseType": "h2Disk",    "prodDatabaseType": "mysql",    "messageBroker": false,    "buildTool": "maven",    "embeddableLaunchScript": false,    "useSass": true,    "clientPackageManager": "npm",    "clientFramework": "angularX",    "clientTheme": "none",    "clientThemeVariant": "",    "creationTimestamp": 1614834465776,    "jhiPrefix": "jhi",    "entitySuffix": "",    "dtoSuffix": "DTO",    "otherModules": [      {        "name": "generator-jhipster-quarkus",        "version": "1.1.1"      }    ],    "enableTranslation": true,    "nativeLanguage": "en",    "languages": ["en"],    "blueprints": [      {        "name": "generator-jhipster-quarkus",        "version": "1.1.1"      }    ]  }}

В этом файле содержатся все ответы на исходные вопросы мастера JHipster, что позволит создать приложение без дополнительных запросов.

Убедитесь, что выполняемая с помощью Keycloak аутентификация OAuth 2.0/OIDC действительно работает

Помимо прочих файлов JHipster Quarkus создаёт файлы для инструментального средства Docker Compose, помогающие загрузить среду разработки, настроенную для вашего только что созданного приложения. При этом даже выполняется импорт данных Keycloak, заданных по умолчанию для пользователей и приложений, и вам не придётся делать это самим.

https://gitlab.webant.ru/russia_quiz/frontend/-/merge_requests

Если Keycloak запущен и работает, вы сможете выполнить вход в систему. Запустите ваше приложение с помощью Maven:

./mvnw

Вы можете видеть, что приложение запускается за 3,351 с, также выводится обширный список расширений Quarkus (включая oidc). Перейдите по адресу http://localhost:8080 в предпочитаемом вами браузере и нажмите ссылку sign in (вход).

Вы будете перенаправлены в Keycloak для входа в систему. При запросе идентификационных данных введите admin/admin.

После успешного прохождения аутентификации вы будете перенаправлены обратно в ваше приложение Quarkus.

Как работает поддержка протокола аутентификации OAuth 2.0 в JHipster Quarkus

Одной из проблем при разработке blueprint-схем JHipster, таких как Quarkus, является необходимость найти правильный баланс между повторным использованием существующих механизмов и добавлением индивидуальных реализаций.

С самого первого дня своего появления JHipster осуществлял интеграцию всех современных платформ для создания веб-приложений (Angular, React или даже Vue.js), совмещая их с фактически стандартными Java-технологиями, такими как Spring Boot и Spring Cloud.

В реализации JHipster Quarkus OAuth 2.0 за основу взято URI-перенаправление login/oauth2/code/oidc, предполагающее использование платформы аутентификации Spring Security. Настоятельно рекомендуем выполнять аутентификации на стороне сервера, поскольку это намного безопаснее (так как у браузера нет необходимости управлять какими-либо идентификационными данными и хранить какие-либо маркеры доступа).

В среде JHipster Quarkus, чтобы повторно использовать без изменений клиентские приложения и реализовать на серверной стороне механизм аутентификации по протоколу OAuth 2.0, в бэкенде должны быть открыты два HTTP-маршрута. Вот почему в JHipster Quarkus предусмотрен контроллер UserOauth2Controller и настраиваемые свойства Quarkus OIDC, позволяющие обеспечить правильную работу всего механизма.

Это своего рода интеграционная ловушка, на которую разработчики не хотят тратить время. Коллективный разум сообщества JHipster, объединяющий мнения самых эффективных разработчиков, объясняет высокую популярность и качество данной платформы.

Объединение JHipster Quarkus с сервисом Okta

Из этого раздела вы узнаете, как использовать сервис Okta в качестве провайдера протокола аутентификации OAuth 2.0/OIDC. В Okta предусмотрены два варианта для конфигурирования OIDC-приложения. Это можно выполнить либо в консоли разработчика, либо с помощью инфраструктуры Okta CLI.

Использование инфраструктуры Okta CLI для конфигурирования JHipster

Инфраструктура Okta CLI автоматизирует для вас все конфигурации JHipster и Okta. Для установки Okta CLI можно воспользоваться популярными менеджерами пакетов.

macOS (с помощью Homebrew):

brew install --cask oktadeveloper/tap/okta

Linux (с помощью Flatpak):

# Add Flathub repoflatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo# install the packageflatpak install com.okta.developer.CLI# add this to your appropriate dot filealias okta="flatpak run com.okta.developer.CLI"

Windows (с помощью Chocolatey):

choco install okta -version 0.8.0

Вы также можете просто передать его в оболочку bash:

curl https://raw.githubusercontent.com/okta/okta-cli/master/cli/src/main/scripts/install.sh | bash

Если у вас ещё нет аккаунта разработчика в Okta, откройте терминал, перейдите в каталог приложения Quarkus и выполните okta register. Если у вас есть аккаунт, выполните okta login.

$ okta registerFirst name: DanielLast name: PetismeEmail address: daniel.petisme@gmail.comCompany: OktaCreating new Okta Organization, this may take a minute:OrgUrl: https://dev-9323263.okta.comAn email has been sent to you with a verification code.Check your emailVerification code: 232819New Okta Account created!Your Okta Domain: https://dev-9323263.okta.comTo set your password open this link:https://dev-9323263.okta.com/welcome/drpt2SjbRAPR-gvVHhnm

Если у вас уже есть аккаунт разработчика Okta, выполните команду okta login. Затем из каталога приложения Quarkus выполните okta apps create jhipster. Примите предлагаемые по умолчанию предложения для перенаправления URI.

$ okta apps create jhipsterApplication name [okta-jhipster-quarkus-example]:Redirect URICommon defaults:  Spring Security - http://localhost:8080/login/oauth2/code/okta  Quarkus OIDC - http://localhost:8080/callback  JHipster - http://localhost:8080/login/oauth2/code/oidcEnter your Redirect URI(s) [http://localhost:8080/login/oauth2/code/oidc, http://localhost:8761/login/oauth2/code/oidc]:Enter your Post Logout Redirect URI(s) [http://localhost:8080/, http://localhost:8761/]:Configuring a new OIDC Application, almost done:Created OIDC application, client-id: 0oa5ozjxyNQPPbKc65d6Creating Authorization Server claim 'groups':Adding user daniel.petisme@gmail.com to groups: [ROLE_USER, ROLE_ADMIN]Creating group: ROLE_USERCreating group: ROLE_ADMIN

Конфигурация приложения Okta записывается здесь: /Users/daniel/workspace/okta-jhipster-quarkus-example/.okta.env

ПРИМЕЧАНИЕ: идентификаторы URI, перенаправленные http://localhost:8761*, предназначены для реестра JHipster, который часто используется при создании микросервисов с помощью JHipster. В инфраструктуре Okta CLI они добавляются по умолчанию. Они не требуются для приложения, создаваемого в этой вводной статье, но если их оставить, то никакого вреда точно не будет.

Инфраструктура Okta CLI создаст файл .okta.env в текущем каталоге. Если посмотреть на него, то вы увидите, что в нём содержатся несколько ключей и значений, предназначенных для протокола OIDC.

$ cat .okta.envexport QUARKUS_OIDC_AUTH_SERVER_URL="http://personeltest.ru/aways/dev-9323263.okta.com/oauth2/default"export QUARKUS_OIDC_CLIENT_ID="0oa5ozjxyNQPPbKc65d6"export QUARKUS_OIDC_CREDENTIALS_SECRET="KEJ0oNOTFEUEFHP7i1TELLING1xLm1XPRn"export QUARKUS_OIDC_AUTHENTICATION_REDIRECT_PATH="/login/oauth2/code/oidc"export JHIPSTER_OIDC_LOGOUT_URL="http://personeltest.ru/aways/dev-9323263.okta.com/oauth2/default/v1/logout"

Установите файл, чтобы задать переменные среды, и запустите ваше приложение с помощью Maven.

source .okta.env./mvnw

Обязательно добавьте \*.env в ваш файл .gitignore, чтобы в коммиты не попадал ваш секрет клиента.

Как только приложение будет запущено, в окне в режиме инкогнито откройте http://localhost:8080 и выполните вход. Вам будет предложено ввести свои идентификационные данные Okta.

После успешного прохождения аутентификации вы будете перенаправлены обратно в ваше приложение. На начальной странице должен отображаться ваш адрес электронной почты.

Инфраструктура Okta CLI упрощает конфигурирование JHipster и выполняет следующие полезные операции.

  1. Создаётся приложение OIDC с правильным перенаправлением URI.

  2. Заводятся группы ROLE_ADMIN и ROLE_USER, требуемые для JHipster.

  3. Ваш текущий пользователь добавляется в группы ROLE_ADMIN и ROLE_USER.

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

Но вдруг вы не любите работать из командной строки? Не паникуйте, тут есть кому прикрыть вашу спину! Инфраструктура Okta CLI проста в использовании, но для тех, кому это необходимо, есть возможность выполнить настройки с помощью интерфейса пользователя. Именно поэтому я так подробно рассматриваю каждый шаг по настройке приложения OIDC, работающего с JHipster Quarkus.

Использование консоли разработчика Okta для настройки JHipster

Если у вас нет аккаунта разработчика Okta, необходимо нажать ссылку sign up (вход). Тут нет ничего сверхсложного: просто укажите имя, фамилию, адрес электронной почты, выберите надёжный пароль и всё вы готовы двигаться дальше. Выполнив вход в систему, вы попадаете в консоль разработчика:

Разверните вложенное меню Applications (Приложения) на панели навигации слева, затем выберите в меню Applications > Create App Integration (Приложения > Создать интеграцию приложений), чтобы запустить мастер создания приложений.

Выберите OIDC и Web Application. Затем нажмите кнопку Next (Далее).

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

  • Name (Имя): можете указать любое имя на свой вкус, но разве JHipster Quarkus чем-то не подходит?

  • Login redirect URIs (Перенаправление URI при входе): определяет, будет ли Okta выполнять перенаправление в браузере клиента после аутентификации. Установите для него http://localhost:8080/login/oauth2/code/oidc, именно это значение настроено по умолчанию.

  • Logout redirect URIs (Перенаправление URI при выходе): http://localhost:8080 указывается, куда будет перенаправляться пользователь после выполнения выхода.

  • Group assignments (Назначения групп): определяет, какие группы могут использовать это приложение.

Используйте предлагаемые значения по умолчанию для всех остальных настроек. Завершив ввод требуемых настроек для своего приложения, нажмите Save (Сохранить). На экране отобразятся подробные сведения о вашем приложении.

Наиболее важными являются следующие значения:

  • идентификационные данные клиента (ID и секрет клиента). Они позволяют приложению Java выполнять аутентификацию в сервисах Okta для последующей обработки потоков аутентификации и авторизации пользователей;

  • домен Okta, из которого Quarkus будет выводить конечные точки адресов URL для протоколов OAuth/OIDC.

Создание групп пользователей

Теперь настало время для создания групп пользователей. По умолчанию для JHipster требуются две следующие группы:

  • ROLE_USER: для аутентифицированных пользователей;

  • ROLE_ADMIN: для аутентифицированных пользователей с административными правами для этого приложения.

В консоли разработчика выберите в меню Directory > Groups (Каталог > Группы). Нажмите Add Group (Добавить группу) и создайте группу ROLE_ADMIN

Теперь добавьте группу ROLE_USER.

Всё, вы завершили создание групп, теперь займёмся добавлением пользователей.

Создание пользователей

Чтобы увидеть различие между обычными и административным пользователями, создайте пользователей в каждой из групп. Используя консоль разработчика Okta, выберите в меню Directory > People (Каталог > Люди). Нажмите Add Person (Добавить человека). Начните с создания пользователя Administrator.

Основным моментом тут является привязка пользователя Administrator к группе ROLE_ADMIN. Чтобы определить исходный пароль, который пользователь должен будет изменить, исключительно в демонстрационных целях для данной вводной статьи использована стратегию выбора пароля Set by Admin (Задаётся администратором). В реальном проекте я рекомендую использовать стратегию Set by User (Задаётся пользователем) с активацией по электронной почте. Теперь добавим обычного пользователя.

Убедитесь, что пользователь User входит в группу ROLE_USER. Очень важно использовать действующий адрес электронной почты, так как он может понадобиться при восстановлении пароля. Выберите в меню Applications > JHipster Quarkus (Приложения > JHipster Quarkus) и нажмите Assignments (Назначения). Назначьте группам пользователей, которых только что создали.

Добавление заявки на группы к маркеру ID

Последнее, о чём необходимо вам позаботиться, это настроить заявку на группы, включающую группы пользователей в маркер ID. Выберите в меню Security > API (Безопасность > API), а затем нажмите default (по умолчанию). Щёлкните Claims > Add Claim (Заявки > Добавить заявку). Введите следующие значения:

  • Name (Имя): groups (группы);

  • Include in token type (Включить тип маркера): ID Token (Маркер ID);

  • Value type (Тип значения): groups (группы);

  • Filter (Фильтр): Matches regex with a value of .* (Соответствует регулярному выражению со значением .*).

Нажмите Create (Создать). Конфигурация Okta для JHipster готова!

Настройка Quarkus OIDC для Okta

В этот момент необходимо, чтобы приложение JHipster Quarkus уже было запущено и для использования Keycloak в качестве провайдера идентификационных данных для него. Давайте изменим настройки для работы с Okta. Во-первых, необходимо выйти из веб-приложения JHipster, чтобы предотвратить конфликт файлов cookie. Выберите Account > Sign Out (Аккаунт > Выход).

ПРИМЕЧАНИЕ: можно оставить приложение запущенным. Quarkus поддерживает работу в так называемом режиме для разработки (Dev Mode), обеспечивающем горячую перезагрузку любых исходных или ресурсных файлов при каждом их обновлении. Это исключительно удобно!

Откройте для редактирования файл src/main/resources/application.properties и найдите в нём раздел со следующей конфигурацией OIDC.

# OAuth 2.0 and OIDCquarkus.oidc.enabled=truequarkus.oidc.auth-server-url=http://localhost:9080/auth/realms/jhipster/%dev.quarkus.oidc.client-id=web_app%dev.quarkus.oidc.credentials.secret=web_appquarkus.oidc.application-type=hybridquarkus.oidc.authentication.scopes=profile,address,email,address,phone,offline_accessquarkus.oidc.authentication.cookie-path=/quarkus.oidc.authentication.redirect-path=/login/oauth2/code/oidcquarkus.oidc.authentication.restore-path-after-redirect=falsejhipster.oidc.logout-url=http://localhost:9080/auth/realms/jhipster/protocol/openid-connect/logout%test.quarkus.oidc.client-id=dummy%test.quarkus.oidc.application-type=service%test.jhipster.oidc.logout-url=some-dummy-logoutUrl

Показанные выше значения предназначены для Keycloak. Вами необходимо обновить следующие свойства, чтобы интегрировать своё приложение с Okta;

  • quarkus.oidc.auth-server-url: корневой URL для API Okta, полученный из домена приложения OIDC;

  • quarkus.oidc.client-id: ID клиента для приложения OIDC;

  • quarkus.oidc.credentials.secret: секрет клиента для приложения OIDC;

  • jhipster.oidc.logout-url: в JHipster браузер будет запускать выход из системы. Серверная сторона должна предоставлять эту информацию (пока её невозможно получить с помощью OIDC-поиска).

После того как вы обновите этот файл, ваши настройки должны выглядеть примерно так:

# OAuth 2.0 and OIDCquarkus.oidc.enabled=truequarkus.oidc.auth-server-url=https://dev-9323263.okta.com/oauth2/defaultquarkus.oidc.client-id=0oaajhdr9q9jxbBM95d6quarkus.oidc.credentials.secret=NEVERSHOWSECRETSquarkus.oidc.application-type=hybridquarkus.oidc.authentication.scopes=profile,address,email,address,phonequarkus.oidc.authentication.cookie-path=/quarkus.oidc.authentication.redirect-path=/login/oauth2/code/oidcquarkus.oidc.authentication.restore-path-after-redirect=falsejhipster.oidc.logout-url=https://dev-9323263.okta.com/oauth2/default/v1/logout

Перезапустите приложение, перейдите по адресу http://localhost:8080. Нажмите sign in (вход) и вы будете перенаправлены на страницу входа Okta.

Введите admin в качестве имени пользователя и пароль, который вы задали. Okta определит, что это ваш первый вход в систему, и предложит сменить пароль. После выполнения вы будете перенаправлены в ваше приложение Quarkus.

Переход в нативный формат с помощью Quarkus и GraalVM

Заключительным шагом в данной вводной статье будет упаковка приложения Java в виде нативного выполняемого файла (оптимизированного для используемого микропроцессора). И снова JHipster надёжно прикрывает ваши тылы, делая для вас всё необходимое. Просто выполните в Maven команду package с профилем native:

./mvnw package -Pnative -DskipTests

Вы знаете, мы знаем, все знают: мы пропустили этап тестирования в этом демонстрационном примере всё работает идеально. Но, пожалуйста, никогда не пропускайте тестирование в условиях реальной промышленной разработки.Если в используемый вами JDK не включён GraalVM, вы получите ошибку:

[error]: Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception: java.lang.RuntimeException: Cannot find the `native-image` in the  GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image`

Самым простым способом решения этой проблемы будет применение SDKMAN для установки Java 11 с GraalVM:

sdk install java 21.0.0.2.r11-grl

Затем выполните gu install native-image:

$ gu install native-imageDownloading: Component catalog from www.graalvm.orgProcessing Component: Native ImageDownloading: Component native-image: Native Image  from github.comInstalling new component: Native Image (org.graalvm.native-image, version 21.0.0.2)

Как только процесс завершится, перезапустите команду package в Maven:

./mvnw package -Pnative -DskipTests

На нативную компиляцию требуется немало времени. У вас может возникнуть желание прогуляться, выпить чашку-другую кофе или несколько раз отжаться. Но, если вы предпочитаете остаться за компьютером, можно потратить время на следующее:

  • ознакомиться с проектом JHipster Quarkus Blueprint;

  • исследовать сайт для разработчиков Okta;

  • изучить эти великолепные руководства по Quarkus.

Спустя примерно три минуты нативный выполняемый файл должен быть готов:

Запустите его как нативный выполняемый файл, используя команду target/*runner:

Ваше старое доброе Java-приложение запустится через 1 секунду! Помните, я рассказывал о приросте памяти? Ниже привожу команду, как посмотреть потребление памяти в мегабайтах:

$ ps -o pid,rss,command | grep --color jhipster | awk '{$2=int($2/1024)"M";}{ print;}'30951 46M ./target/jhipster-1.0.0-SNAPSHOT-runner31433 0M grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --color jhipster

Ваше приложение потребляет менее 50 МБ памяти. Перейдите по адресу http://localhost:8080 и убедитесь, что всё исправно работает. Теперь наслаждайтесь своим успехом!

Дальнейшая разработка с помощью JHipster Quarkus

Надеюсь, вы получили удовольствие от данной вводной статьи, пройдя все этапы создания нативного приложения и применяя для этого Java, Quarkus и JHipster. Не правда ли, впечатляет, как JHipster и Okta CLI делают для вас основную тяжёлую работу?! Вы можете найти пример, созданный в этой вводной статье, на GitHub. Если вы заинтересованы в дополнительном изучении blueprint-схем Quarkus, посмотрите проект generator-jhipster-quarkus, также размещённый на GitHub.

Совсем недавно Java считали медленным языком программирования, а вернее было бы сказать, что медленной была виртуальная машина Java, но это не помешало языку стать одним из самых востребованных по версии TIOBE. Сегодня Java становится всё быстрее, а это значит, что она ещё надолго сохранит способность конкурировать с новыми языками; кроме того, в последнее время наметилась тенденция к упрощению синтаксиса Java. Если вам интересен этот язык и работа с ним в перспективе, то вы можете посмотреть на программу обучения нашего курса по Java, где студенты за 14 месяцев получают всестороннюю подготовку к трудоустройству в качестве Java-разработчика.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Перевод Работаем с NPM реестром из Java

29.07.2020 08:08:02 | Автор: admin
image
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 APIRxJNPMService rxJnpmService = JNPMService.instance().getRxService() //RXJava API


Пример использования:

//Общая информаци о NPM реестреSystem.out.println(JNPMService.instance().getRegistryInfo());//Заполучить и напечатать информацию о последней версии VUESystem.out.println(JNPMService.instance().getPackageInfo("vue").getLatest());//Напечатать описание пакета vue@2.6.11System.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);});


Если у вас какой-то специфичный случай, который был не описан здесь пожалуйста, дайте знать!
Подробнее..
Категории: Node.js , Java , Api , Npm , Repository

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru