Если вы пишете инструменты или библиотеки JavaScript, вам следует объединить свой код перед публикацией.

Несколько часов назад Азер Кочулу освободил свою коллекцию модулей от npm »после спора о товарном знаке. Один из них - 11-строчная утилита для помещения нулей перед строками - сильно зависел от других модулей, включая Babel, от которого сильно зависит весь Интернет.

И вот интернет сломался.

Люди подтвердили свои предубеждения:

Люди запаниковали:

И люди рассердились:

Я сочувствую всем, кто здесь участвует. Ситуация отстойная для всех, в том числе для Азера (который никому из вас ни черта не задолжал!). Но чтение ветки GitHub должно вызвать у вас глубокое раздражение, потому что эта проблема очень легко решается.

Объедините свой код, даже если он не для браузера

Напомним:

  1. левая панель не была опубликована
  2. Babel использует фиксированные версии своих зависимостей, одна из которых (транзитивно) была left-pad
  3. При установке Babel вы также устанавливаете все его зависимости (и их зависимости)
  4. Поэтому все старые версии Babel были закрыты (пока левая панель не была неопубликована)
  5. Люди обвиняют Азера

Ключевым элементом здесь является номер 3. Предположим, что вместо перечисления всех этих зависимостей в package.json, Babel был распространен в виде пакета со всеми его зависимостями. (Я выбираю Babel, потому что это самая заметная жертва фиаско левых сторонников, но Babel просто придерживается норм сообщества - норм, которые я собираюсь оспорить, на самом деле не имеют смысла.)

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

Полное раскрытие: как создатель Rollup, конечно я думаю, вам следует объединить свой код в пакет. Но для целей этой статьи не имеет значения, используете ли вы Rollup, Webpack, Browserify или что-то еще - важна идея, а не детали.

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

Установка занимает крошечную долю времени

Вы пробовали установить пакет со многими зависимостями через npm3 при плохом соединении? Это до смешного ужасный опыт, во многом потому, что наблюдается комбинаторный взрыв сетевых запросов на зависимости и зависимости зависимостей. В зависимости от авторов этих зависимостей, вы можете загружать README, тесты и другие различные губбины, которые вам не нужны. Если у пакета нет зависимостей, npm может загрузить один файл tar и покончить с ним.

Для совершенно ненаучной демонстрации возьмем три пакета, которые выполняют примерно похожие вещи - Rollup, Webpack и Browserify. Сколько времени занимает новая установка каждого из них?

Для установки Browserify, имеющей 47 непосредственных зависимостей (многие из которых, разумеется, есть свои собственные), потребовалось 26,4 секунды. Webpack, у которого 15, занял почти 30 секунд. На накопительный пакет, который содержит 3 пакета (все для встроенного интерфейса командной строки - мы могли бы удалить даже их), потребовалось менее 5. Это гораздо лучший опыт для разработчиков.

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

Вы тратите меньше места на диске

Как следствие вышесказанного, если вы не загружаете сотни транзитивных зависимостей (плюс вышеупомянутые губбины), неудивительно, что это занимает меньше места на диске. При использовании npm3 Browserify занимает 12,2 МБ для 2459 элементов. Webpack занимает 21Мб на 3093 товара! Накопительный пакет занимает 2,5 МБ для 209 элементов.

(Конечно, я понимаю, что их нельзя сравнивать напрямую. Но вы меня поняли.)

Запуск быстрее

Если вы не слышали, Node`s` require` is dog slow . И я собираюсь снова выбрать Babel - когда вышел Babel 6, который был преобразован в миллионы крошечных модулей, я обнаружил, что он совершенно непригоден для использования, потому что для запуска потребовалось около 3 секунд. Это настоящий облом, когда ты привык к хорошему быстрому построению. Обновление до npm3 резко улучшило ситуацию из-за плоской структуры node_modules, но это досадно, потому что npm2 работает намного быстрее.

Ваша библиотека более стабильна

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

(Если вы думаете, что semver защищает вас от такого рода поломок, вы, вероятно, не слишком долго этим занимались.)

Вы усложняете злодеям быть злодеями

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

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

Вы не заставляете своих пользователей объединять его за вас

В частности, если ваша библиотека подходит для использования в браузере, вероятно, что кому-то другому понадобится связать ее в какой-то момент. И тогда все становится сложно, потому что они должны иметь правильную комбинацию инструментов и конфигурации, чтобы избежать каких-либо сюрпризов. Команда PouchDB обнаружила, что их пользователи испытывают трудности с использованием Webpack со своей библиотекой, потому что он был построен с учетом Browserify. В конце концов, у них настал момент прийти к Иисусу, и они начали рассылать один гигантский файл index.js.

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

Хорошо, я понимаю вашу точку зрения, но вы явно ошибаетесь

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

  1. Объединение предотвращает дедупликацию. Если две из моих зависимостей, foo и bar, используют третью зависимость, baz, я не хочу, чтобы baz появлялся дважды в моем коде.
  2. Вааааааххххх, я не люблю строить ступеньки.

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

Первое возражение более убедительно. Это, конечно, не возражение против более слабого утверждения о том, что вы обязательно должны объединить свой собственный код (т.е. если у вас много файлов в lib / или src /, вы должны объединить их) , но у него есть некоторые достоинства против утверждения, что вы должны объединить свой собственный код плюс свои зависимости.

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

Я продана! Что теперь?

Узнайте, как использовать сборщик модулей (желательно Rollup или JSPM! Но также Webpack или Browserify) и используйте обработчик предварительной публикации npm для объединения вашего кода. Скажите своим друзьям, чтобы они сделали то же самое.

И перестаньте обвинять таких людей, как Азер, когда ваши вещи ломаются.