Привет! Меня зовут Света, я фронтенд-разработчик отдела спецпроектов в KTS.

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

У нас много библиотек: с общими утилитами и хуками, стилями, классами базовых сторов, с утилитами для ВК- и ОК-приложений. Во всех этих библиотеках нужно постоянно дополнять и заменять какие-то элементы, а после этого тестировать библиотеки на работоспособность.

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

Оглавление

В чём сложность работы с библиотеками и чем удобен наш способ подключения

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

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

Мы постепенно покрываем автотестами все свои внутренние библиотеки, но на их написание и проверку нужно потратить много времени. А npm/yarn link позволяет видеть эффект от изменений в коде библиотеки сразу же в проекте, к которому она подключена. Поэтому этот способ облегчает тестирование в реальном времени, но и не исключает важность автотестов.

Подключение и тестирование библиотеки по шагам

Вам потребуется сама библиотека и подопытный проект, на котором она будет тестироваться. Оба проекта в качестве пакетного менеджера должны использовать yarn одной версии. Ниже в статье будет также рассмотрен более частный случай, когда в зависимостях библиотеки и проекта есть React. Если у вас нет библиотеки для теста, можно взять react-testing-library. Тестовый проект можно создать с помощью одного из шаблонов Vite. Установите библиотеку в проект.

Для подключения библиотеки к проекту в большинстве случаев достаточно команды yarn link. Но бывает, что функционала yarn для линковки недостаточно, и тогда на помощь приходит npm link.

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

npm 10.2.3, yarn 1.22.21, node 20.10.0, терминал bash внутри VS Code 1.84.2, macOS Sonoma 14.1.1

Для некоторых команд могут потребоваться права администратора. В этих случаях на MacOS и Linux вы получите ошибку вида “Permission denied”, а на Windows такую: “Access is denied. You do not have sufficient privileges”. Для решения припишите sudo перед командой на macOS и Linux или откройте терминал с правами администратора на Windows.

Давайте представим, что тестируемая библиотека — @ktsstudio/test-library, это имя можно найти в поле "name" в package.json в проекте с библиотекой. Она склонирована в папку test-library, а тестовый проект лежит, соответственно, в папке test-project. Предполагается, что в тестовом проекте эта библиотека уже установлена.

Структура папок проектов примерно следующая:

Смотреть

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

1 — Установка зависимостей и сборка библиотеки

Для этого есть два способа.

Короткий способ — внутри проекта с библиотекой выполните команду: 

yarn install && yarn build

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

npm 10.2.3, yarn 1.22.21, node 20.10.0, терминал bash внутри VS Code 1.84.2, macOS Sonoma 14.1.1.

В этом окружении работают оба способа. Если после выполнения всех последующих пунктов первый работает некорректно, сначала попробуйте удалить изменения в yarn.lock и переустановите зависимости в используемых проектах. Для этого можно использовать следующие команды, а затем снова выполнить шаги 1-4 текущего раздела:

root-folder $ cd test-project
test-project $ git restore yarn.lock && rm -rf node_modules/ && yarn install
test-project $ cd ../test-library
test-library $ git restore yarn.lock && rm -rf node_modules/ && yarn install

Если этот способ всё равно не работает или работает некорректно, воспользуйтесь следующим вариантом.

 

Длинный способ:

  • внутри проекта с библиотекой выполните команду: 

yarn install && yarn build

  • Скопируйте сформировавшуюся папку dist и package.json в любой другой каталог вне каталога с тестовым проектом. Это понадобится по двум причинам: 

    • во избежание возможных проблем с созданием ссылок на библиотеки

    • чтобы файл и папка соответствовали структуре библиотеки в node_modules тестируемого проекта 

Смотреть
Структура библиотеки в node_modules тестируемого проекта
Структура библиотеки в node_modules тестируемого проекта

  • Когда вы скопировали папку dist и package.json в другой каталог, нужно выполнить команду yarn install из этого каталога, чтобы установить в нём зависимости.  Например, в папке long-way вы создали папку test-library-like, чтобы протестировать библиотеку @ktsstudio/test-library.

    Так может выглядеть структура каталога после выполнения шагов выше:

Смотреть

  • После этого все команды, относящиеся к проекту с разрабатываемой библиотекой, нужно будет выполнять из этого же каталога. В нашем примере — из test-library-like.

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

2 — Создание ссылки на экземпляры пакетов React

Если библиотека @ktsstudio/test-library под капотом использует библиотеку React, нужно учесть один нюанс. 

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

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

Для этого выполните команду npm link для react (название пакета в node_modules) и для @types/react, если они используются в библиотеке. 

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

Внутри проекта с разрабатываемой библиотекой выполните команды:

  • npm link <путь до тестового проекта>/node_modules/react

  • npm link <путь до тестового проекта>/node_modules/@types/react

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

  • npm link ../test-project/node_modules/react

  • npm link ../test-project/node_modules/@types/react

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

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

npm list -g --depth=0

Найдите в списке экземпляр react и экземпляр его типов @types/react, где путь указывает на пакеты в составе вашего тестового проекта. На примере нашего тестового проекта это может выглядеть так:

$ npm list -g --depth=0
/Users/user/.npm-global/lib
├── @types/react@18.2.4 -> ./../../projects/test-project/node_modules/@types/react
└── react@18.2.0 -> ./../../projects/test-project/node_modules/react

В файловой системе или в интерфейсе IDE связанные экземпляры пакетов в папке node_modules тестируемой библиотеки будут иметь иконку ярлыка. Если на шаге установки зависимостей и сборки вы выбрали длинный способ, вот как может выглядеть папка node_modules:

Примечания:

  • ❗В некоторых случаях использование npm link не нужно.
    Это зависит от того, какие именно функции библиотеки используются в проекте. Например, если тестируется библиотека, которая использует react, но в тестовом проекте используются только те методы из библиотеки, в которых ничего не импортируется из react — npm link не обязателен.

  • ❗Версия node должна быть одинаковая в библиотеке и в тестовом проекте, иначе тестовый проект не увидит библиотеку по npm link.
    Проверить версию можно в package.json в поле engines.node. Если такого поля нет, в проекте будет использоваться глобально установленная версия node на вашем компьютере. Узнать глобальную версию можно с помощью команды node -v. Если глобально установленная версия node отличается от той, что указана в package.json, ее можно легко изменить с помощью менеджера версий, например NVM. Переключиться на другую версию node, которая указана в IDE или в файле .nvmrc в проекте, можно с помощью команды nvm use (предварительно потребуется установить nvm). Эта команда устанавливает нужную версию только для текущего терминала.

3 — Создание глобальной ссылки на библиотеку

Внутри каталога с библиотекой выполните команду yarn link. Так вы создадите глобальную ссылку, которую можно будет использовать в тестовом проекте. 

Убедиться, что ссылка на библиотеку создана, можно через команды:

  • Для macOS — ls ~/.config/yarn/link/<имя библиотеки>

  • Для Windows — ls ~/AppData/Local/Yarn/Data/link/<имя библиотеки>

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

$ ls ~/.config/yarn/link/@ktsstudio/test-library
dist    node_modules    package.json    src    tsconfig.json    yarn.lock

Точное название библиотеки можно найти — или задать для новосозданной библиотеки — в package.json в поле "name". 

4 — Подключение тестируемой библиотеки к проекту

Для этого внутри тестового проекта выполните команду: yarn link "@ktsstudio/test-library". Так вы заставите yarn смотреть именно в разрабатываемую библиотеку с нужными изменениями.

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

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

5 — Тестирование

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

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

Ошибки

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

  • Can't resolve 'react' / Cannot read properties of null (reading 'useCallback') и другие похожие. Для решения в терминале библиотеки выполните:

     npm link <путь до тестового проекта>/node_modules/react

    Подробнее об этой ошибке можно узнать в документации React.

Смотреть ошибку

'ErrorBoundary' cannot be used as a JSX component и другие похожие. Подобная ошибка возникает из-за несоответствия версий пакета @types/react в тестовом проекте и в библиотеке. Для решения в терминале библиотеки выполните:

npm link <путь до тестового проекта>/node_modules/@types/react

  • It looks like there are several instances of "styled-components". Такого вида предупреждение может возникать в консоли при тестировании библиотеки, которая под капотом использует styled-components. Для решения попробуйте:

    1. Внутри проекта с библиотекой выполните команду npm link <путь до тестового проекта>/node_modules/styled-components/

    2. Перезапустите тестовый проект. Возможно, переустановите зависимости

    3. При завершении тестирования не забудьте отвязать ссылку: npm uninstall -g styled-components

      Подробнее об ошибке в документации Styled Components.

  • В качестве общего решения, если что-то не работает, в тестовом проекте и библиотеке попробуйте эти шаги:

    1. Удалите изменения в yarn.lock: git restore yarn.lock

    2. Переустановите зависимости: rm -rf node_modules && yarn install

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

Вернемся к примеру с разрабатываемой библиотекой @ktsstudio/test-library. После окончания тестирования необходимо удалить созданные ссылки на библиотеки, чтобы при работе над другим проектом эти ссылки не привели к путанице и потенциальным конфликтам с зависимостями.

Для очищения ссылок нужно выполнить следующие шаги:

  1. Если вы создавали ссылки на экземпляры пакетов React — внутри каталога с собранной библиотекой или внутри тестового проекта выполните команду npm uninstall -g react @types/react. Так вы отвяжете React и его типы от собранной библиотеки. Если вы тестируете несколько библиотек, достаточно прописать это один раз.

  2. Если внутри проекта с библиотекой вы вызывали npm link, удалите оттуда изменения в yarn.lock.

  3. Внутри тестового проекта выполните команду yarn unlink "@ktsstudio/test-library"
    ❗Примечание: это нужно на всякий случай — чтобы убрать ссылку на ранее собранную библиотеку.

  4. Внутри каталога с библиотекой выполните yarn unlink
    Так вы удалите глобальную ссылку на пакет с разрабатываемой библиотекой. Убедиться, что ссылка удалена, можно, выполнив:

    • Для macOS — ls ~/.config/yarn/link

    • Для Windows — ls ~/AppData/Local/Yarn/Data/link

    • Посмотрите содержимое корневого каталога по соответствующему пути. В каталоге может присутствовать папка "@ktsstudio" (или другой префикс в зависимости от вашей тестируемой библиотеки), но папки с конкретной разрабатываемой/тестируемой библиотекой ("test-library" в нашем случае) быть не должно.

  5. Внутри тестового проекта выполните yarn install --force, чтобы восстановить зависимости.

Заключение

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

Сталкивались ли вы с какими-нибудь проблемами при использовании npm/yarn link? Поделитесь в комментариях своим опытом и идеями.

Другие статьи про React:

Собираем свою библиотеку для SSR на React
Создаем текстовый редактор на React.js
Игра с голосовым управлением на React и Phaser
Что нового в react-router v6
React Drag & Drop: «Игра в бутылки»

Другие статьи про JavaScript:

Как сверстать письмо, чтобы оно дошло до получателя таким, как задумано
Роадмэп по современному фронтенду от KTS / Хабр (habr.com)
Кастомизируем VS Code для веб-разработки / Хабр (habr.com)
Чек-лист фронтендера при разработке рекламного спецпроекта / Хабр (habr.com)
Как yarn v3 и философия Zero Installs помогли нам сократить длительность ci/cd пайплайна в 3 раза / Хабр (habr.com)

Комментарии (12)


  1. JSmitty
    17.06.2024 15:11

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

    Охх, как вспомню - так вздрогну. Кмк единственный маршрут, которым в 2024 идут практически все - использовать монорепо. Более-менее безболезненно, и отлично решает проблемы синхронизации версий зависимостей. По решению "кто куда ходит, кто нет" - отлично работает CODEOWNERS.


    1. kurakina Автор
      17.06.2024 15:11
      +3

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


      1. JSmitty
        17.06.2024 15:11
        +1

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


        1. kurakina Автор
          17.06.2024 15:11
          +7

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


  1. ToLive
    17.06.2024 15:11

    yalc попробуйте, я с ним проблем не знаю, очень удобно. https://github.com/wclr/yalc


    1. kurakina Автор
      17.06.2024 15:11
      +2

      за рекомендацию спасибо, поглядим


  1. Psychosynthesis
    17.06.2024 15:11

    Эточё, гайд по использованию символических ссылок?

    А зачем тут yarn?


    1. kurakina Автор
      17.06.2024 15:11
      +1

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


  1. stayacid
    17.06.2024 15:11

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


    1. kurakina Автор
      17.06.2024 15:11
      +1

      Если имеется в виду положить папку с библиотекой рядом с проектом и обращаться к ней без симлинка, то будет долго и неудобно во всех файлах проекта в импортах менять пути с обращения к установленной библиотеке на пути к папке с измененной библиотекой (условно, import smth from "@lib" заменять на import smth from "../../../@lib"). Кажется, что придется, например, вносить эту папку с либой в исключения для линтеров и тайпскрипта


      1. yarkov
        17.06.2024 15:11

        А зачем этим заниматься, если можно алиасы настроить?


      1. stayacid
        17.06.2024 15:11

        Не, я про выполнение пункта 3 и линковку библиотеки через yarn link "@ktsstudio/test-library". Можно сделать "yarn add @ktsstudio/test-library@link:путь_до_папки_с_test_library_относительно_текущей_папки" и оно прекрасно будет работать, даже hot reload на изменения в test_library будет реагировать. Потом отменяете изменения в package.json и yarn.lock, указываете нужную версию библиотеки test_library, запускаете yarn install (или просто yarn), получаете ошибки из-за линковки, еще раз запускаете yarn install и библиотека нормально встает из пакета