Skip to main content

Yarn 2プラグイン入門

著者
Plugin feature

2020年4月13日

0 分で読めます

Yarn 2やそのプラグインアーキテクチャに興味がある方は、まずyarnpkg.comのプラグインアーキテクチャに関するドキュメントを読み、サイトのYarn 2プラグインチュートリアルも確認してみてください。

Yarn 2を初めて使う方は、私のブログ記事Yarn 2入門をぜひお読みください。

Yarn 2プラグインを作成する

まずはプラグインを開発する環境を整えます。Yarn 2で管理する新しいプロジェクトの作成から始めましょう。

mkdir my-app
cd my-app
yarn policies set-version berry
yarn init

これで、Yarn 2で管理されるまっさらなnpmパッケージプロジェクトができました。

Yarnの実行プロセスの一部としてプラグインを実行することをYarnに伝えるには、.yarnrc.ymlという新しいファイルを作成し、開発するプラグインへのパスを指定してプラグイン定義を追加します。

plugins:
 - ./plugin-hook-install-hello-world.js

続いて、プラグインのコードを見ていきましょう。Yarnの処理フェーズにフックする機能を作成します。ここではafterAllInstalledにフックし、その後コンソールにメッセージを出力します。プラグインのコードは次のようになります。

const afterAllInstalled = project => {
 console.log("Everything is installed 🎉");
};

module.exports = {
 name: `plugin-hello-world`,
 factory: require => {
   return {
     default: {
       hooks: {
         afterAllInstalled
       }
     }
   };
 }
};

プラグインをテストするには、CLIからyarnコマンドを実行するだけです。次のように出力されます。

➜ yarn
➤ YN0000: ┌ Resolution step
➤ YN0000: └ Completed in 0s
➤ YN0000: ┌ Fetch step
➤ YN0000: └ Completed in 0s
➤ YN0000: ┌ Link step
➤ YN0000: └ Completed in 0.01s
Everything is installed 🎉
➤ YN0000: Done in 0.04s

すばらしい!これで独自のYarn 2プラグインができました。

Yarn 2プラグインでは、yarnの実行ファイルへのコマンド追加や、Yarnが参照する新しいリゾルバーやフェッチャーの追加、さらには相互に連携するyarnプラグインの作成など、さまざまなことができます。

Yarnにコマンドを追加するYarn 2プラグインの作成方法を知りたい方は、手順を解説している公式ドキュメントYarn pluginsをご覧ください。

Yarn 2プラグインで依存関係の詳細情報にアクセスする

先ほどの例で見たように、Yarnのフックの中には、プロジェクトに関してYarnが収集した詳細なメタデータを含む、グローバルな公開オブジェクトを提供して、より深く連携できるものがあります。

次の例では、再びafterAllInstalledフックと連携します。このフックには、

追加のパラメーターとして公開Projectオブジェクトが渡されます。このパラメーターを使うと、依存関係、パッケージマニフェスト、ワークスペース情報など、Yarnがプロジェクトについて収集したすべての情報にアクセスできます。

次のplugin-hook-install-hello-brave-world.jsプラグインの例では、このオブジェクトの情報を変数_に格納し、内容をJSONファイルに書き出して、さらに詳しく調べられるようにします。

const fs = require("fs");
const util = require("util");

module.exports = {
 name: `plugin-hello-brave-world`,
 factory: require => {
   return {
     default: {
       hooks: {
         afterAllInstalled(_) {
           console.log("🎉 afterAllInstalled hook invoked");
           fs.writeFileSync("afterAll.json", util.inspect(_, false, 10));
         }
       }
     }
   };
 }
};

Projectオブジェクトの構造はpackages/berry-core/sources/Project.tsで定義されています。オブジェクトのルートレベルにあるキーの一部を以下に示します。

Project {
 configuration: {},
 cwd: {},
 workspaces: Map {},
 storedResolutions: Map {},
 storedDescriptors: Map {},
 storedPackages: Map {},
 storedChecksums: Map {},
 ...
}

これらのstored*キーを使うと、依存関係を把握し、このプロジェクトのツリーを構築できます。

簡単にするため、依存関係がdebugだけのnpmプロジェクトでこのプラグインを実行します。debug自体もmsに依存しています。

storedPackagesオブジェクトは、このプロジェクトの依存関係一覧を取得するためのエントリーポイントです。また、このオブジェクトには実際のプロジェクト名のエントリーも含まれており、workspace: keyという参照で示されます。

storedPackagesのMapで、debugの依存関係がどのように表示されるか見てみましょう。

'a1f870a4a95fff67eeac2388e06345982ebf1149c710f8efe18fe5d1967fec40db60987022a5c6fe55708167d1add4b63bc8ad6f755e20c6fba140d721180595' => { identHash:
       'd027b0b474dd440d333c0ae6200111acff30aa5931aeacf1841b1eb9212edea377606a118ff0f8675b69eabe9ff00db4f2f16659519c82810c6b534f9b8ad82d',
      scope: undefined,
      name: 'debug',
      locatorHash:
       'a1f870a4a95fff67eeac2388e06345982ebf1149c710f8efe18fe5d1967fec40db60987022a5c6fe55708167d1add4b63bc8ad6f755e20c6fba140d721180595',
      reference: 'npm:4.1.1',
      version: '4.1.1',
      languageName: 'node',
      linkType: 'hard',
      dependencies:
       Map {
         '299701b4a21f15498c990a6ec8bf49b0331f01e1d610cefaa6f7040bab1be634be89d1462245207c21d1334b31690861f303a0a71aa94f80445d80b9ee37eaf6' => { identHash:
            '299701b4a21f15498c990a6ec8bf49b0331f01e1d610cefaa6f7040bab1be634be89d1462245207c21d1334b31690861f303a0a71aa94f80445d80b9ee37eaf6',
           scope: undefined,
           name: 'ms',
           descriptorHash:
            '0742408cf974a8f1cd5081a9aec19656dc8016eec3c6e2358f302902e6f1f87241f601911cb45e855f3b0d26160c1520715ae79904c5ed1d29f5383ab906440e',
           range: 'npm:^2.1.1' } },
      peerDependencies: Map {},
      dependenciesMeta: Map {},
      peerDependenciesMeta: Map {},
      bin: Map {} },
    '9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33' => { identHash:
       '299701b4a21f15498c990a6ec8bf49b0331f01e1d610cefaa6f7040bab1be634be89d1462245207c21d1334b31690861f303a0a71aa94f80445d80b9ee37eaf6',
      scope: undefined,
      name: 'ms',
      locatorHash:
       '9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33',
      reference: 'npm:2.1.2',
      version: '2.1.2',
      languageName: 'node',
      linkType: 'hard',
      dependencies: Map {},
      peerDependencies: Map {},
      dependenciesMeta: Map {},
      peerDependenciesMeta: Map {},
      bin: Map {} },

debugのエントリーには、識別用の一意なハッシュ、解決済みバージョンなどのメタデータ、そしてdebugが依存するパッケージを列挙した、依存関係のネストされたオブジェクトがあります。ただし、ネストされた依存関係オブジェクトに表示されるmsは解決済みではなく、バージョン範囲だけが示されています。

ネストされた依存関係を解決するには、descriptorHashを使ってstoredResolutionsマップを参照します。内容は次のとおりです。

storedResolutions:
  Map {
    '35f50d92512bedba8fbf78bdeae4f2bce60934a798f5e0a0ab58b087fb7dc73880c7ffee2e135c15e48ca70687336bbdf4163e75da3f90b55da5ba5e41d36051' => '35f50d92512bedba8fbf78bdeae4f2bce60934a798f5e0a0ab58b087fb7dc73880c7ffee2e135c15e48ca70687336bbdf4163e75da3f90b55da5ba5e41d36051',
    'ee78c55248c8a07a4079ce749bc462124c923d7b990785b50150c417b2821ca3363767b8dc304d80d4feae3faf9f95e1c2c46cebc582b81fe70a8f45a69b6377' => 'a1f870a4a95fff67eeac2388e06345982ebf1149c710f8efe18fe5d1967fec40db60987022a5c6fe55708167d1add4b63bc8ad6f755e20c6fba140d721180595',
    '0742408cf974a8f1cd5081a9aec19656dc8016eec3c6e2358f302902e6f1f87241f601911cb45e855f3b0d26160c1520715ae79904c5ed1d29f5383ab906440e' => '9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33' },

msのdescriptorHashは0742408cf974a8f1cd5081a9aec19656dc8016eec3c6e2358f302902e6f1f87241f601911cb45e855f3b0d26160c1520715ae79904c5ed1d29f5383ab906440eです。これはstoredResolutionsの3番目の要素で、ハッシュ9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33を参照しています。このハッシュはさらにstoredPackagesマップ内の依存関係エントリーに対応しており、今度はmsの解決済みメタデータを取得できます。

'9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33' => { identHash:
       '299701b4a21f15498c990a6ec8bf49b0331f01e1d610cefaa6f7040bab1be634be89d1462245207c21d1334b31690861f303a0a71aa94f80445d80b9ee37eaf6',
      scope: undefined,
      name: 'ms',
      locatorHash:
       '9455a02525b0e2c50eca4e204d71900a775107249d4245c1ea1f95e3f124c8a1d27484b29ccea934895da4d0273b22dc95cefd0561c39490cd7c86ab4404ca33',
      reference: 'npm:2.1.2',
      version: '2.1.2',
      languageName: 'node',
      linkType: 'hard',
      dependencies: Map {},
      peerDependencies: Map {},
      dependenciesMeta: Map {},
      peerDependenciesMeta: Map {},
      bin: Map {} },

Yarn 2プラグインのまとめ

Yarn 2はまだ新しく、ユーザーへの普及やコミュニティからのフィードバックを取り入れている段階です。プラグインに取り組むには絶好のタイミングです。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。

カテゴリー: