Живой ли пакет: шесть вопросов вместо звёзд и загрузок
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Каждый пакет из npm — обещание жить с ним годами, а звёзды и загрузки на вопрос «жив ли он» не отвечают: растёт весь реестр, и растут даже пакеты, которые сами авторы давно держат в режиме поддержки. В статье про npm выбор уместился в минуту на страницу пакета; здесь та минута разложена на шесть вопросов по порядку и функцию, которая по записанному снимку реестра отвечает на первые три. Снимок: сентябрь 2026.
Первый отсев — не про пакет, а про задачу. Многие популярные пакеты появились, когда в языке и браузерах нужного не было. Встроенное с тех пор догнало, а строки в package.json остались.
| Зачем раньше ставили пакет | Чем это закрыто сейчас | Baseline |
|---|---|---|
| глубокая копия объекта | structuredClone(value) | Widely |
| случайный идентификатор UUID v4 | crypto.randomUUID() | Widely |
| группировка списка по признаку | Object.groupBy(list, fn) | Widely |
| уникальные значения | new Set(list) | Widely |
| показ даты человеку | Intl.DateTimeFormat | Widely |
| запросы к серверу | встроенный fetch | Widely |
Последний столбец — статус Baseline, отметка на страницах справочника MDN: работает ли возможность во всех основных браузерах. Newly — во всех, но меньше 30 месяцев, Widely — во всех и дольше 30 месяцев, Limited — не во всех. Если вы читали пролог, мерило знакомо по статье о том, почему у HTML нет номера версии.
Отсюда правило, записанное в учебнике один раз — здесь. Widely — пакет не нужен. Newly — берут встроенное с оговоркой про старые устройства: у части пользователей браузер старее, чем кажется. Limited — библиотека допустима, но с пометкой «уйдёт, когда…».
Все шесть строк таблицы — Widely, и пакет для этих задач не проходит нулевой вопрос, каким бы живым он ни был. Оговорка одна: встроенное закрывает типичный случай, а не любой. structuredClone не копирует функции, а Intl.DateTimeFormat показывает дату, но не разбирает строки и не считает «через два месяца». Поэтому вопрос звучит так: умеет ли платформа то, что нужно моей задаче.
Пакет, который повторяет встроенное, приходится обновлять, проверять и объяснять новичкам, а работу за него давно делает браузер.
Следующий вопрос — выпускает ли пакет кто-нибудь. Ответ лежит в реестре, и npm view достаёт его в терминале. Спросим про idb — маленькую обёртку над хранилищем браузера IndexedDB, moment — библиотеку дат и request — HTTP-клиент для Node:
У idb поле time.modified приходится на день последнего выпуска, у moment — тоже: 15 сентября 2026 года вышла версия 2.31.0 с исправлением уязвимости, первая с декабря 2023-го. А у request там 17 июля 2026 года, хотя версия latest вышла 11 февраля 2020-го: реестр правил метаданные пакета, а выпуска не было. Поэтому возраст считают по дате выпуска версии latest — полю time[версия], — а не по time.modified, которое двигает любая правка метаданных.
У того же request стоит пометка deprecated: автор сам снял пакет с поддержки и написал об этом в реестре. Это прямой отказ, дальше спрашивать не о чем.
idb — пример другого рода: законченный не значит брошенный. Маленькая библиотека может молчать годами, потому что в ней нечего чинить, и по датам этого не отличить. Отличает человек: открывает issues и смотрит, отвечает ли сопровождающий. Функция в конце статьи такой пакет не отвергает, а передаёт вам. Один сопровождающий — тоже не отказ, а пометка о риске: уйдёт он, и заменить его будет некому.
Тег latest ставит автор, и npm install без версии берёт именно его. Обычно тег стоит на последней стабильной версии, но не обязан:
У prisma на latest стоит кандидат в выпуск — суффикс -rc значит release candidate, — а последняя стабильная версия вышла тремя неделями раньше. npm install prisma без версии принесёт в dependencies кандидата. Проверяют это до установки: смотрят dist-tags и нет ли в номере версии дефиса с суффиксом.
grep оставляет из длинного списка дат только строки с нужными версиями, и он здесь не для красоты: короткая форма npm view prisma time.7.10.0 печатает пустоту, потому что точки в имени поля npm читает как вложенность.
Дата правки и дата выпуска — разные поля. Возраст пакета считают по выпуску версии latest, а time.modified двигает любая правка метаданных.
Цена пакета — не размер его папки в node_modules, а то, что сборщик положит в бандл. Как разбиралось в статье о статичности импортов, неиспользованное выкидывается только при подходящей форме import. Поэтому одна библиотека с двумя разными строками import может весить в разы по-разному — и не всегда в ту сторону, которую подсказывает интуиция. Цифры такого случая — на полке браузера, в разделе о проверке данных.
Вторая часть цены — лицензия. В поле license реестра обычно стоит короткое имя вроде MIT, но бывает и строка SEE LICENSE IN LICENSE — ссылка на собственный текст внутри пакета. Условия в таком тексте бывают какими угодно, поэтому их читают до установки.
На телефоне цена считается иначе: важны не столько килобайты, сколько другое — заработает ли пакет в готовом приложении для разработки, которое берёт у проекта только JavaScript, или ради него придётся собирать своё приложение вместе с кодом для iOS и Android. Это разобрано на полке React Native и Expo.
И безопасность: каждая зависимость — чужой код с правами вашего проекта, и истории left-pad и event-stream из статьи про npm — не древность.
Реестр помнит больше, чем отдаёт. Вот что осталось от конца марта 2026 года у HTTP-клиента axios:
В time, где записаны даты всех выпусков, есть две версии от 31 марта, а в versions, списке того, что можно установить, их нет. В эти версии попал вредоносный код, и их сняли; предупреждение GitHub GHSA-fw8c-xr5c-95f9 вышло меньше чем через три часа после первой из них. Отсюда два вывода: package-lock.json с уже проверенными версиями коммитят, а свежий выпуск популярного пакета не спешат ставить в первые же часы.
Цена пакета — то, что уедет к пользователю, условия лицензии и требования к клиенту, а не папка в node_modules.
Пусть даты в проекте показывает библиотека. Если её импортирует единственный ваш модуль dates.js и отдаёт наружу две свои функции, formatDue и addMonthsSafe, то для смены библиотеки правят один файл, а сотню остальных даже не открывают. Если же те же вызовы разбросаны по сотне файлов, библиотека уже стала частью устройства проекта, и заменить её — значит переписать проект. Это и есть вопрос 4: меняется ли зависимость на границе.
Некоторые пакеты за границей не удержать по самой их природе. В следующих частях встретятся четыре таких рода: стили, записанные классами прямо в разметку; своя модель данных на всё приложение; отдельный язык схемы с шагом генерации кода; чужой сервер, где лежат данные ваших пользователей. Каждый бывает оправдан, но каждый превращает смену пакета из правки в переезд.
Противоположность — общий интерфейс на границе, который понимают сразу несколько библиотек. Пример — Standard Schema: его реализуют библиотеки проверки данных, и код, принимающий схему, не знает, какая библиотека за ней стоит. Разобран он на полке React и Next, в разделе о формах.
Честная оговорка: этот вопрос совпадает с укладом автора учебника. Поэтому на каждой полке есть раздел «Не на полке и почему» — с причиной, которую можно проверить, а не со вкусом.
Пакет, который живёт за одним вашим модулем, меняется за вечер. Пакет, вызовы которого разошлись по всему проекту, меняется переездом.
Последний вопрос — о вас. Если механизм библиотеки вы хоть раз собирали сами, видно, где кончаются десять своих строк и начинается работа, ради которой библиотеку пишут. Такие стены в учебнике уже встречались или скоро встретятся:
| Своя сборка | Где стена | Что за ней |
|---|---|---|
console.assert в первых тестах | тесты хочется держать в отдельных файлах, с названиями и отчётом по всему набору | тест-фреймворки Jest и Vitest |
сеттеры Date в разделе «Прибавить день» | 31 января плюс месяц уезжает в март, а «через два месяца» с месяцами разной длины и чужие часовые пояса своими строками уже не пишутся | библиотеки дат вроде date-fns, в самом языке — Temporal |
render() из состояния в проекте части II | пересборка разметки целиком сбрасывает набранный текст и фокус | React, Vue и их родня |
Отсюда правило полок: пакет попадает на полку либо со ссылкой на свою сборку в учебнике, либо с фразой «своей сборки в учебнике нет, потому что…».
Своя сборка не заменяет библиотеку. Она показывает, за что вы платите, когда библиотеку ставите.
На вопросы 0–2 отвечают данные, и их можно доверить функции. Ниже — снимок шести пакетов: дата выпуска версии latest, пометка deprecated и загрузки за одну и ту же неделю сентября 2026 и 2025 года. Живых запросов в коде нет — только выдержки из реестра.
Ниши знать заранее не нужно, хватит фразы о каждом пакете. uuid делает случайные идентификаторы. request — HTTP-запросы в Node, где теперь есть встроенный fetch. moment — даты. idb — маленькая обёртка над хранилищем браузера IndexedDB из части II. formik — формы в React, до которых дойдёт часть IV. zod — проверка данных, она появится в части III. Прогноз: на каком вопросе остановится каждый из шести?
Порядок решает. uuid жив и растёт, но отсеян на нулевом вопросе: для идентификаторов v4 хватает crypto.randomUUID(), а нужен ли пакет ради других версий UUID, которых в платформе нет, решает задача. request остановился на первом вопросе по deprecated, и загрузки его уже не спасают. Поле platform у него пустое, хотя в Node теперь есть встроенный fetch: хватит ли его, зависит от того, что делает ваш код, а пометка автора закрывает вопрос для любого проекта.
Самое интересное — moment и formik. Машинный вердикт у них один: по датам оба живы, а ниша уходит — рост ×1.03 и ×1.02 при медиане ×2.69 функция посчитала сама. Но свежая дата у moment — выпуск 2.31.0, который закрыл уязвимость, а сопровождающие по-прежнему называют проект legacy — в заметке о статусе и в шапке документации. Вопрос 1 смотрит только на дату, а выпуск с исправлением проходит его так же, как выпуск с новыми возможностями, — поэтому проект в режиме поддержки отсеялся только на втором вопросе. idb остался на первом вопросе с вердиктом «решает человек»: маленькую обёртку проверяют по issues. zod прошёл три вопроса, и это ещё не «брать»: дальше цена, устройство проекта и своя сборка — на них функция не отвечает.
TODAY — константа, а не new Date(): с датой из часов у idb через месяц прибавился бы месяц тишины при прежних загрузках, а с 10 декабря formik перешёл бы со второго вопроса на первый — вывод поплыл бы. Двигает его и сам реестр: снимок днём раньше, от 14.09.2026, остановил бы moment на первом вопросе — 32 месяца без выпуска, — а на второй его перевёл выпуск 2.31.0. С константой снимок остаётся правдой о 15.09.2026 навсегда — так же, как листинг с датой.
Функция отвечает только на то, что видно в данных. Законченный пакет от брошенного, цену и устройство проекта отличает человек.
Советов «берите такой-то пакет» в этой статье нет намеренно: версии и цифры стареют за год, а метод — нет. Имена с версиями и датой проверки живут на трёх полках: браузера — в части II, React и Next — в части IV, React Native и Expo — в части V.
Каждая строка полки отвечает на вопросы этой статьи: когда пакет не нужен и что умеет платформа, где своя сборка, снимок с версией, весом или ценой на дату проверки — и пометка «Пересмотреть, если…», условие, при котором строку пора переписать. Скрипт, который сам спрашивает реестр о зависимостях проекта, появится позже — в части VI, когда код выйдет из браузера в Node.
Метод — на годы, имена — на год. Поэтому у имён на полке есть дата, а у шести вопросов её нет.
- Шесть вопросов идут по порядку, и первый «нет» отвечает за весь пакет: платформа, живость, доля в нише, цена, устройство проекта, своя сборка.
- Вопрос 0 — про задачу: у Widely пакет не нужен, у Newly берут встроенное с оговоркой, у Limited библиотека допустима с пометкой «уйдёт, когда…».
- Возраст считают по выпуску версии latest (
time[версия]), а не поtime.modified;deprecated— прямой отказ; тег latest бывает предварительной версией. Законченный пакет от брошенного отличает человек. - Загрузки растут у всего реестра — от транзитивных установок, CI и агентов, поэтому рост сравнивают с медианой: ×1,2 и ниже значит, что ниша уходит.
- Цена — вес после сборщика, условия лицензии и требования к клиенту на телефоне; хорошая зависимость меняется на границе одного вашего модуля, а своя сборка показывает стену, за которой она нужна.
- Функция отвечает на вопросы 0–2 по датированному снимку, остальные три — ваши; имена с версиями и датой проверки живут на полках частей II, IV и V.
1/4Пакет повторяет возможность, у которой в Baseline стоит Newly. Как поступить по правилу вопроса 0?
2/4У пакета time.modified — вчерашнее число, а версия latest вышла три года назад. Сколько пакет живёт без выпуска?
3/4Загрузки пакета за год выросли в 1,1 раза, а медиана роста популярных пакетов — 2,69. Что это значит?
4/4Функция health вернула для пакета «вопросы 0–2 пройдены». Что дальше?
Строка для dayjs
Добавьте в снимок ещё одну библиотеку дат — dayjs. Значения сняты тем же днём, что и остальные: выпуск версии latest — 17.08.2026, загрузки за неделю 07–13.09.2026 — 51 400 889, за ту же неделю год назад — 28 316 560. Сначала предскажите строку, которую напечатает функция, потом проверьте запуском и сравните dayjs с moment.
В снимок добавляется одна строка:
Функция напечатает dayjs — вопросы 0–2 пройдены: дальше цена, устройство проекта и своя сборка. Встроенной замены в снимке нет — Intl.DateTimeFormat только показывает дату, — выпуск меньше месяца назад, рост ×1,82. Это ниже медианы ×2,69, но выше порога ×1,2.
Интереснее сравнение внутри ниши. У moment за тот же год рост ×1,03 — функция остановила его на втором вопросе, — у dayjs ×1,82: оба пакета решают одну задачу, и доля dayjs в ней растёт. Именно это и называется «доля в нише, а не загрузки»: по свежим выпускам и миллионам в неделю оба пакета выглядят живыми.
Возраст по дате правки
В снимке появилось поле modified — значение time.modified из того же npm view, — а к шести пакетам добавился седьмой: react-scripts, сборка React-приложения из Create React App, до которого дойдёт часть IV. Сделайте ошибку, от которой предостерегает раздел о вопросе 1: пусть health считает месяцы тишины от modified, а не от released. Какой пакет «оживёт», на каком вопросе он теперь остановится и почему этот ответ всё равно получен неправильно?
Меняется одна строка в health:
Оживает react-scripts. Правка метаданных 10.04.2026 превращает 53 месяца тишины в пять, пакет проходит первый вопрос и падает на втором: react-scripts — вопрос 2: рост ×0.63 при медиане ×2.69 — ниша уходит.
Остальные строки не меняются. У uuid, moment, idb, formik и zod правка пришлась на день выпуска. У request дата правки тоже свежая, июль 2026 года, но функция отсеяла его по deprecated раньше, чем дошла до дат.
Вердикт у react-scripts по-прежнему отрицательный, но причина подменена: вместо «давно не выпускался — решает человек» функция говорит «жив, но теряет долю». С таким ответом легко не заглянуть в блог React, где 14 февраля 2025 года Create React App объявили устаревшим для новых приложений. Ошибка в выборе поля не всегда меняет итог — она меняет, какой вопрос вы себе зададите.
Первый «нет»
Для каждого случая назовите вопрос, на котором пакет остановится, и что делать дальше: отказаться, передать решение человеку или идти к следующему вопросу.
| № | Случай |
|---|---|
| 1 | пакет глубокого копирования объектов: выпуск на прошлой неделе, загрузки за год выросли втрое |
| 2 | библиотека графиков: последний выпуск полтора года назад, на issues отвечают в тот же день, рост ×2,4 |
| 3 | пакет для чтения файлов Excel с пометкой deprecated и ростом ×3 |
| 4 | компонент таблицы с сортировкой и фильтрами: свежий выпуск, рост ×4 и строка SEE LICENSE IN LICENSE в поле license |
1. Вопрос 0. structuredClone — Widely, и свежесть с ростом тут ничего не меняют. Пакет нужен, только если задача требует того, чего structuredClone не умеет, — например, копировать функции.
2. Вопрос 1, но не отказ: полтора года без выпуска — машинный ответ «решает человек». Человек смотрит issues, видит живого сопровождающего и идёт дальше. Вопрос 2 пакет тоже проходит: ×2,4 — выше порога ×1,2 и близко к медиане. Дальше — цена.
3. Вопрос 1, прямой отказ. Встроенного разбора Excel нет, так что нулевой вопрос пакет проходит, но пометку deprecated рост не отменяет — скорее всего, пакет скачивают транзитивно или по старым инструкциям.
4. Вопросы 0–2 пройдены: сортировать и фильтровать таблицу браузер сам не умеет, выпуск свежий, рост выше медианы. Остановка на вопросе 3 — цене. Текст лицензии читают до установки: в нём могут быть условия, которых у MIT нет.