Spec-Zone.ru › Yarn 3

Plug'n'Play

API PnP

Вы автор библиотеки, стремящийся сделать её совместимой со стратегией установки Plug'n'Play? Хотите использовать API PnP для чего-то крутого? Если ответ на любой из этих вопросов положительный, обязательно посетите страницу API PnP после прочтения введения!

Plug'n'Play, представленный в сентябре 2018 года, — это инновационная стратегия установки для Node. Основанная на предыдущих работах в других языках (например, autoload для PHP), она обладает интересными характеристиками, которые расширяют обычный require рабочий процесс CommonJS почти полностью в обратной совместимости.

  • Проблема node_modules

  • Исправление node_modules

  • Инициализация PnP

  • Режим PnP loose

    • Оговорка
  • Альтернативы

    • Таблица совместимости

      • Нативная поддержка
      • Поддержка через плагины
      • Несовместимо
  • Часто задаваемые вопросы

    • Почему не использовать import maps?
    • Пакеты хранятся внутри архивов Zip: как получить доступ к их файлам?
    • Режим резервного копирования

Проблема node_modules

Способ установки ранее был простым: при запуске yarn install Yarn генерировал каталог node_modules, который Node затем мог использовать благодаря встроенному алгоритму разрешения Node. В этом контексте Node не должен был знать ничего о том, что такое "пакет": он работал только с файлами. "Существует ли этот файл здесь? Нет: Хорошо, давайте посмотрим в родительском node_modules. Существует ли он здесь? По-прежнему нет: Хорошо...", и так продолжалось до тех пор, пока не находился нужный файл. Этот процесс был чрезвычайно неэффективным по нескольким причинам:

  • Каталоги node_modules обычно содержали огромное количество файлов. Их создание могло составлять более 70% времени, необходимого для запуска yarn install. Даже имеющиеся установки не помогали, так как менеджерам пакетов все равно приходилось сравнивать содержимое node_modules с тем, что должно быть.

  • Поскольку генерация node_modules была ресурсоёмкой операцией ввода-вывода, у менеджеров пакетов было мало возможностей для оптимизации, кроме простого копирования файлов — и даже если бы они могли использовать жёсткие ссылки или копирование при записи при необходимости, им все равно потребовалось бы сравнить текущее состояние файловой системы перед выполнением нескольких системных вызовов для манипулирования диском.

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

  • Даже во время выполнения Node разрешение должно было выполнить множество stat и readdir вызовов, чтобы выяснить, откуда загрузить каждый необходимый файл. Это было крайне неэффективно, и это одна из причин, почему запуск приложений Node занимал так много времени.

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

Исправление node_modules

Yarn уже знает всё о вашей зависимости — он даже устанавливает её на диск. Так почему Node должен искать, где находятся ваши пакеты? Вместо этого, менеджер пакетов должен сообщать интерпретатору о расположении пакетов на диске и управлять зависимостями между пакетами и даже версиями пакетов. Вот почему был создан Plug'n'Play.

В этом режиме установки (по умолчанию начиная с Yarn 2.0) Yarn генерирует один файл .pnp.cjs вместо обычного каталога node_modules, содержащего копии различных пакетов. Файл .pnp.cjs содержит различные карты: одна связывает имена и версии пакетов с их расположением на диске, а другая связывает имена и версии пакетов с их списком зависимостей. С этими таблицами поиска Yarn может мгновенно сообщить Node, где найти любой пакет, который ему нужен, если он входит в дерево зависимостей и если этот файл загружен в вашей среде (подробнее об этом в следующем разделе).

Этот подход имеет множество преимуществ:

  • Установка теперь практически мгновенна. Yarn нужно сгенерировать только один текстовый файл (вместо потенциальных десятков тысяч). Главной проблемой становится количество зависимостей в проекте, а не производительность диска.

  • Установка более стабильна и надёжна благодаря сокращению операций ввода-вывода. Особенно в Windows (где запись и удаление файлов в группах могут вызывать различные непредвиденные взаимодействия с Windows Defender и аналогичными инструментами) ресурсоёмкие операции node_modules были более склонны к ошибкам.

  • Идеальная оптимизация дерева зависимостей (идеальный хостинг) и предсказуемое создание пакетов.

  • Сгенерированный файл .pnp.cjs может быть добавлен в ваш репозиторий в рамках усилий Zero-Installs, устраняя необходимость запуска yarn install в первую очередь.

  • Более быстрый запуск приложения! Разрешение Node не нужно так часто просматривать иерархию файловой системы (а скоро и вовсе не потребуется!).

Инициализация PnP

Yarn генерирует один файл .pnp.cjs, который необходимо установить, чтобы Node знал, где найти соответствующие пакеты. Эта регистрация обычно прозрачна: любой прямой или косвенный node команду, выполненную через одну из ваших scripts записей, автоматически зарегистрирует файл .pnp.cjs в качестве зависимости во время выполнения. Для подавляющего большинства случаев использования следующее будет работать так, как вы ожидаете:

{
  "scripts": {
    "start": "node ./server.js",
    "test": "jest"
  }
}

Для некоторых оставшихся крайних случаев может потребоваться небольшая настройка:

  • Если вам нужно запустить произвольный скрипт Node, используйте yarn node в качестве интерпретатора вместо node. Этого будет достаточно, чтобы зарегистрировать файл .pnp.cjs как зависимость во время выполнения.
yarn node ./server.js
  • Если вы работаете в системе, которая автоматически выполняет скрипт Node (например, в Google Cloud Platform (--необходимая ссылка здесь--)), просто подключайте файл PnP в верхней части своего скрипта инициализации и вызывайте его функцию setup.
require('./.pnp.cjs').setup();

В качестве быстрого совета, yarn node обычно просто задает переменную среды NODE_OPTIONS для использования --require опцию Node, связанную с путем к файлу .pnp.cjs. Вы можете легко выполнить эту операцию самостоятельно, если предпочитаете:

node -r ./.pnp.cjs ./server.js
NODE_OPTIONS="--require $(pwd)/.pnp.cjs" node ./server.js

Режим PnP loose

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

Чтобы решить эту проблему, Yarn поставляется с режимом "loose", который заставит связующий модуль PnP работать вместе с хостером node-modules — мы сначала сгенерируем список пакетов, которые были бы подняты на верхний уровень при обычной установке node_modules, затем сохраним этот список как "резервный пул".

Обратите внимание, что поскольку режим loose напрямую вызывает хостер node-modules, он использует ту же самую реализацию, что и реальный алгоритм, используемый node-modules связующим модулем!

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

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

Этот режим предоставляет компромисс между strict связующим модулем PnP и node_modules связующим модулем.

Чтобы включить режим loose, убедитесь, что опция nodeLinker установлена в значение pnp (по умолчанию) и добавьте следующее в ваш локальный файл .yarnrc.yml:

pnpMode: loose

Дополнительная информация об опции pnpMode.

Оговорка

Поскольку мы выводим предупреждения (вместо выбрасывания ошибок) при ошибках разрешения, приложения не могут их перехватывать. Это означает, что общий шаблон попытки require необязательной зависимости peer внутри блока try/catch будет выводить предупреждение во время выполнения, если зависимость отсутствует, даже если это не должно было произойти. Единственное следствие во время выполнения — такое предупреждение может сбить с толку, но его можно безопасно игнорировать.

По этой причине режим PnP loose не будет по умолчанию, начиная с версии 2.1 (как мы изначально планировали). Он по-прежнему будет поддерживаться как альтернатива, чтобы, надеемся, облегчить переход к стандартному и рекомендуемому рабочему процессу: режим PnP strict.

Альтернативы

В годы, предшествовавшие ратификации Plug'n'Play в качестве основного метода установки, другие проекты разрабатывали альтернативные реализации алгоритма разрешения узлов — обычно для обхода недостатков API require.resolve. Примеры включают Webpack (enhanced-resolve), Babel (resolve), Jest (jest-resolve) и Metro (metro-resolver). Эти альтернативы следует рассматривать как устаревшие, заменённые надлежащей интеграцией с Plug'n'Play.

Таблица совместимости

Следующая таблица совместимости даёт представление о состоянии интеграции с различными инструментами сообщества. Обратите внимание, что в ней указаны только инструменты командной строки, так как библиотеки фронтенда (например, react, vue, lodash, ...) не переопределяют разрешение узлов и, следовательно, не нуждаются в специальной логике для использования Plug'n'Play:

Предложить добавление в эту таблицу

Встроенная поддержка

Многие распространённые инструменты фронтенда теперь поддерживают Plug'n'Play встроены!

Название проекта
Примечание
Babel Начиная с resolve 1.9
Create-React-App Начиная с версии 2.0+
ESLint Некоторые проблемы совместимости с общими конфигурациями
Gatsby Поддерживается с версией ≥2.15.0, ≥3.7.0
Gulp Поддерживается с версией 4.0+
Husky Начиная с 4.0.0-1+
Jest Начиная с 24.1+
Next.js Начиная с 9.1.2+
Parcel Начиная с 2.0.0-nightly.212+
Preact CLI Начиная с 3.1.0+
Prettier Начиная с 1.17+
Rollup Начиная с resolve 1.9+
Storybook Начиная с 6.0+
TypeScript Через plugin-compat (включено по умолчанию)
TypeScript-ESLint Начиная с 2.12+
WebStorm Начиная с 2019.3+; см. Editor SDKs
Webpack Начиная с 5+ (плагин доступен для 4.x)

Поддержка через плагины

Название проекта
Примечание
ESBuild Через @yarnpkg/esbuild-plugin-pnp
VSCode-ESLint Следуйте Editor SDKs
VSCode Следуйте Editor SDKs
Webpack 4.x Через pnp-webpack-plugin (встроено начиная с 5+)

Несовместимые

Следующие инструменты не могут использоваться с чистой установкой Plug'n'Play (даже в режиме ослабленного режима).

Важно: Даже если инструмент несовместим с Plug'n'Play, вы всё равно можете включить node-modules плагин. Просто следуйте инструкциям, и вы будете готовы к работе через минуту 🙂

Название проекта
Примечание
Angular Следуйте angular/angular-cli/#16980
Flow Следуйте yarnpkg/berry#634
React Native Следуйте react-native-community/cli#27
Pulumi Следуйте pulumi/pulumi#3586
VSCode Extension Manager (vsce) Используйте форк vsce-yarn-patch с включённым плагином node-modules. Fork необходим до слияния microsoft/vscode-vsce#493, так как vsce в настоящее время использует удалённую команду yarn list
Hugo Hugo ожидает node-modules каталог. Включите плагин node-modules
ReScript Следуйте rescript-lang/rescript-compiler#3276

Этот список поддерживается в актуальном состоянии на основе последней опубликованной версии, начиная с v2. Если вы заметите что-то не то в своём проекте, сначала попробуйте обновить Yarn и проблемный пакет, затем, не стесняйтесь, создать вопрос. И, возможно, PR? 😊

Часто задаваемые вопросы

Почему не использовать import maps?

Yarn Plug'n'Play предоставляет семантические ошибки (объясняя точную причину, по которой пакет недоступен из другого) и понятный JS API для решения различных недостатков require.resolve. Это функции, которые import maps не решат сами по себе. Подробнее об этом в этой теме.

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

Пакеты хранятся внутри архивов Zip. Как я могу получить доступ к их файлам?

При использовании PnP пакеты хранятся и доступны непосредственно внутри архивов Zip из кэша. PnP-среда выполнения (.pnp.cjs) автоматически исправляет модуль fs Node, чтобы добавить поддержку доступа к файлам внутри архивов Zip. Таким образом, вам не нужно делать ничего особенного:

const {readFileSync} = require(`fs`);

// Looks similar to `/path/to/.yarn/cache/lodash-npm-4.17.11-1c592398b2-8b49646c65.zip/node_modules/lodash/ceil.js`
const lodashCeilPath = require.resolve(`lodash/ceil`);

console.log(readFileSync(lodashCeilPath));

Режим обратного вызова

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

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

© 2016–present Yarn Contributors
Licensed under the BSD License.
https://v3.yarnpkg.com/features/pnp

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API