コンテンツへスキップ

プラグイン用環境API

実験的機能

環境 API は実験的です。エコシステムが実験し、それらを基盤として構築できるように、メジャーリリース間での API の安定性は引き続き維持されます。ダウンストリームプロジェクトが新機能を実験し、検証する時間を持った後、将来のメジャーリリースでこれらの新しい API を安定化させる予定です(潜在的な破壊的変更を伴う可能性があります)。

リソース

ぜひフィードバックをお寄せください。

フックにおける現在の環境へのアクセス

Vite 6までは環境がclientssrの2つしかなかったため、Vite APIで現在の環境を識別するにはssrブーリアンで十分でした。プラグインフックは、最後のオプションパラメーターでssrブーリアンを受け取り、いくつかのAPIでは、モジュールを正しい環境に適切に関連付けるためにオプションの最後のssrパラメーターを期待していました(例:server.moduleGraph.getModuleByUrl(url, { ssr }))。

設定可能な環境の登場により、プラグインでそれらのオプションとインスタンスにアクセスする統一された方法ができました。プラグインフックはコンテキスト内でthis.environmentを公開するようになり、以前ssrブーリアンを期待していたAPIは、適切な環境にスコープされるようになりました(例:environment.moduleGraph.getModuleByUrl(url))。

Viteサーバーは共有のプラグインパイプラインを持っていますが、モジュールが処理されるときは常に特定の環境のコンテキストで行われます。environmentインスタンスはプラグインコンテキストで利用可能です。

プラグインはenvironmentインスタンスを使用して、環境の設定(environment.configを使用してアクセス可能)に応じてモジュールの処理方法を変更できます。

ts
  transform(code, id) {
    console.log(this.environment.config.resolve.conditions)
  }

フックを使用した新しい環境の登録

プラグインはconfigフックで新しい環境を追加できます(例えば、RSC用の個別のモジュールグラフを持つためなど)。

ts
  config(config: UserConfig) {
    config.environments.rsc ??= {}
  }

空のオブジェクトで環境を登録するのに十分であり、ルートレベルの環境設定からデフォルト値が適用されます。

フックを使用した環境の設定

configフックが実行されている間は、環境の完全なリストはまだ不明であり、環境はルートレベルの環境設定からのデフォルト値、またはconfig.environmentsレコードを通じて明示的に影響を受ける可能性があります。プラグインはconfigフックを使用してデフォルト値を設定する必要があります。各環境を設定するには、新しいconfigEnvironmentフックを使用できます。このフックは、最終的なデフォルトの解決を含む、部分的に解決された設定を持つ各環境に対して呼び出されます。

ts
  configEnvironment(name: string, options: EnvironmentOptions) {
    if (name === 'rsc') {
      options.resolve.conditions = // ...

hotUpdateフック

  • 型: (this: { environment: DevEnvironment }, options: HotUpdateOptions) => Array<EnvironmentModuleNode> | void | Promise<Array<EnvironmentModuleNode> | void>
  • 参照: HMR API

hotUpdateフックは、プラグインが特定の環境に対してカスタムHMR更新処理を実行できるようにします。ファイルが変更されると、HMRアルゴリズムはserver.environmentsの順序に従って各環境に対して順次実行されるため、hotUpdateフックは複数回呼び出されます。このフックは、次のシグネチャを持つコンテキストオブジェクトを受け取ります。

ts
interface HotUpdateOptions {
  type: 'create' | 'update' | 'delete'
  file: string
  timestamp: number
  modules: Array<EnvironmentModuleNode>
  read: () => string | Promise<string>
  server: ViteDevServer
}
  • this.environmentは、ファイル更新が現在処理されているモジュール実行環境です。

  • modulesは、変更されたファイルによって影響を受ける、この環境内のモジュールの配列です。単一のファイルが複数の提供されるモジュール(例:Vue SFC)にマップされる可能性があるため、配列になります。

  • readは、ファイルのコンテンツを返す非同期読み取り関数です。これは、システムによっては、エディターがファイルの更新を終了する前にファイル変更コールバックが速く発火しすぎることがあり、直接fs.readFileが空のコンテンツを返すため提供されます。渡された読み取り関数はこの動作を正規化します。

このフックは以下のいずれかを選択できます。

  • 影響を受けるモジュールリストをフィルタリングして絞り込み、HMRをより正確にします。

  • 空の配列を返し、完全なリロードを実行します。

    js
    hotUpdate({ modules, timestamp }) {
      if (this.environment.name !== 'client')
        return
    
      // Invalidate modules manually
      const invalidatedModules = new Set()
      for (const mod of modules) {
        this.environment.moduleGraph.invalidateModule(
          mod,
          invalidatedModules,
          timestamp,
          true
        )
      }
      this.environment.hot.send({ type: 'full-reload' })
      return []
    }
  • 空の配列を返し、カスタムイベントをクライアントに送信することで、完全にカスタムのHMR処理を実行します。

    js
    hotUpdate() {
      if (this.environment.name !== 'client')
        return
    
      this.environment.hot.send({
        type: 'custom',
        event: 'special-update',
        data: {}
      })
      return []
    }

    クライアントコードは、HMR APIを使用して対応するハンドラーを登録する必要があります(これは同じプラグインのtransformフックによって注入される可能性があります)。

    js
    if (import.meta.hot) {
      import.meta.hot.on('special-update', (data) => {
        // perform custom update
      })
    }

プラグイン内の環境ごとの状態

同じプラグインインスタンスが異なる環境で使用されるため、プラグインの状態はthis.environmentでキー付けする必要があります。これは、クライアントとSSRモジュールの状態が混ざらないように、ssrブーリアンをキーとして使用してモジュールの状態を保持するために、エコシステムがすでに使用しているのと同じパターンです。Map<Environment, State>を使用して、各環境の状態を個別に保持できます。後方互換性のため、buildStartbuildEndは、perEnvironmentStartEndDuringDev: trueフラグがない場合は、クライアント環境に対してのみ呼び出されることに注意してください。

js
function PerEnvironmentCountTransformedModulesPlugin() {
  const state = new Map<Environment, { count: number }>()
  return {
    name: 'count-transformed-modules',
    perEnvironmentStartEndDuringDev: true,
    buildStart() {
      state.set(this.environment, { count: 0 })
    },
    transform(id) {
      state.get(this.environment).count++
    },
    buildEnd() {
      console.log(this.environment.name, state.get(this.environment).count)
    }
  }
}

環境ごとのプラグイン

プラグインは、applyToEnvironment関数を使用して、適用される環境を定義できます。

js
const UnoCssPlugin = () => {
  // shared global state
  return {
    buildStart() {
      // init per-environment state with WeakMap<Environment,Data>
      // using this.environment
    },
    configureServer() {
      // use global hooks normally
    },
    applyToEnvironment(environment) {
      // return true if this plugin should be active in this environment,
      // or return a new plugin to replace it.
      // if the hook is not used, the plugin is active in all environments
    },
    resolveId(id, importer) {
      // only called for environments this plugin apply to
    },
  }
}

プラグインが環境を認識しておらず、現在の環境にキー付けされていない状態を持っている場合、applyToEnvironmentフックを使用すると、それを簡単に環境ごとにすることができます。

js
import { nonShareablePlugin } from 'non-shareable-plugin'

export default defineConfig({
  plugins: [
    {
      name: 'per-environment-plugin',
      applyToEnvironment(environment) {
        return nonShareablePlugin({ outputName: environment.name })
      },
    },
  ],
})

Viteは、他のフックが不要なこれらのケースを簡素化するためにperEnvironmentPluginヘルパーをエクスポートします。

js
import { nonShareablePlugin } from 'non-shareable-plugin'

export default defineConfig({
  plugins: [
    perEnvironmentPlugin('per-environment-plugin', (environment) =>
      nonShareablePlugin({ outputName: environment.name }),
    ),
  ],
})

applyToEnvironmentフックは、設定時に、現在configResolvedの後に呼び出されます。これは、エコシステム内のプロジェクトがその中でプラグインを変更するためです。環境プラグインの解決は、将来的にconfigResolvedの前に移動される可能性があります。

ビルドフックにおける環境

開発時と同様に、プラグインフックはビルド時にも環境インスタンスを受け取り、ssrブーリアンを置き換えます。これはrenderChunkgenerateBundle、およびその他のビルドのみのフックにも適用されます。

ビルド時の共有プラグイン

Vite 6より前は、プラグインパイプラインは開発時とビルド時で異なる方法で動作していました。

  • 開発時: プラグインは共有される。
  • ビルド時: プラグインは各環境で分離される(異なるプロセスで:vite build、次にvite build --ssr)。

これにより、フレームワークはファイルシステムに書き込まれたマニフェストファイルを介して、clientビルドとssrビルド間で状態を共有することを余儀なくされました。Vite 6では、すべての環境を単一のプロセスでビルドするようになったため、プラグインパイプラインと環境間通信の方法を開発時と合わせることができます。

将来のメジャーリリースでは、完全なアライメントを実現できる可能性があります。

ビルド時にも単一のResolvedConfigインスタンスが共有され、開発時にWeakMap<ResolvedConfig, CachedData>で行っていたのと同じ方法で、アプリケーション全体のビルドプロセスレベルでのキャッシュが可能になります。

Vite 6では、後方互換性を維持するために小さなステップを踏む必要があります。エコシステムプラグインは現在、設定にアクセスするためにenvironment.config.buildではなくconfig.buildを使用しているため、デフォルトでは環境ごとに新しいResolvedConfigを作成する必要があります。プロジェクトは、builder.sharedConfigBuildtrueに設定することで、完全な設定とプラグインパイプラインの共有をオプトインできます。

このオプションは、最初はごく一部のプロジェクトでのみ機能するため、プラグイン作成者はsharedDuringBuildフラグをtrueに設定することで、特定のプラグインが共有されるようにオプトインできます。これにより、通常のプラグインと状態を簡単に共有できます。

js
function myPlugin() {
  // Share state among all environments in dev and build
  const sharedState = ...
  return {
    name: 'shared-plugin',
    transform(code, id) { ... },

    // Opt-in into a single instance for all environments
    sharedDuringBuild: true,
  }
}

MIT ライセンスで公開。(083ff36d)