2025 年になって Vercel 界隈(主に Next.js)では最近ディレクティブの定義が増えてきました。
"use client";"use server";"use cache";"use workflow";"use step";
"use strict"; でしか目にしなかったディレクティブがいつの間にかたくさんコードの中に溢れている。。
今までおまじないにしか思っていなかったこのディレクティブが何をされてるのか気になってきたので調べてみました。
ディレクティブとは
直訳では「指示・指令・命令」です。
ECMAScript の仕様を確認してみると、 "Directive Prologue" として言及されていて、「文字列("文字列"や'文字列')とセミコロン(;)で終わる命令文」「関数やスクリプト、モジュールの最初に書かれる、文字列だけの命令の集まり」と定義されています。
ではこの命令がどういったものかというと、
Javascript の "use strict"; の場合だと JS エンジンに対して「このコードは Strict モードで実行されるよ」と命令しているわけです。
Next.js で書く分においては「このコードはこのランタイムで実行されるよ」というランタイムの境界をはっきりさせるために書かれます。
| ディレクティブ | 含まれるバンドル(実行環境) |
|---|---|
| "use client"; | ブラウザ用クライアントバンドル |
| "use server"; | Next.js サーバーバンドル(Node.js) |
| "use workflow"; | Vercel Workflow バンドル(サンドボックス VM) |
| "use step"; | Vercel Workflow バンドル(サンドボックス VM) |
| "use cache"; | Next.js サーバーバンドル(RSC 実行環境) |
結論として、
ディレクティブを定義することで、「このコードはここから一定の意味を持って実行されるよ」という実行モードを命令しているわけです。
ただ、独自定義のディレクティブは JS エンジンで読み取ろうとしてもただの文字列であり基本的に無視されます。
以下のようなコードは実行してもディレクティブの部分は無視されます。
#!/usr/bin/env node
"use custom directive"; // 実行しても無視されてエラーなども出ない
console.log("run javascript everywhere")
ディレクティブはどのように解釈されているのだろうか?
実行時は JS エンジンに無視されるディレクティブがどのように実行モードを決定できているのでしょうか?
答えは単純で、独自定義のディレクティブはフレームワーク側でコンパイラやバンドラーが解釈するです。("use strict";は仕様なのでJSエンジンが解釈します)
Next.js の場合、Turbopack によって解釈され、SWC のプラグインを使うことでコードの書き換えが行われます。
たとえば、"use workflow";を例に Next.js のビルドプロセスをみていきます。
実際にビルドプロセスでは、2 つの異なる変換が行われます。
実行時(Turbopack)
Turbopack のバンドラーが"use workflow";や"use step";を含むファイルを検出し、loader 経由で SWC プラグインを mode: 'client' で実行してクライアント側バンドル用に変換します。
"use workflow";を使っている部分は以下のようにディレクティブ削除され、エラーを返すコードに置換されています。
これによってブラウザ環境でワークフローの関数が実行されることを防ぎます。
// Next.js内のコード
export async function myWorkflow(a, b) {
"use workflow";
return a + b;
}
// (変換後)クライアント側でワークフローの関数コードは実行できないようにエラーを返す
export async function myWorkflow(a, b) {
throw new Error("You attempted to execute workflow myWorkflow function directly. To start a workflow, use start(myWorkflow) from workflow/api");
}
myWorkflow.workflowId = "workflow//path/to/file.ts//myWorkflow";
"use workflow";を使うとき、nextConfig をwithWorkflow関数でラップして有効化するのはそのためです。
https://useworkflow.dev/docs/api-reference/workflow-next/with-workflow
ビルド時(esbuild)
一方で、ワークフローを Vercel 環境で実行する場合は、
esbuild を使って SWC プラグインをmode: 'workflow'で実行することで、サーバー側のワークフロー実行バンドル(.well-known/workflow/v1/flow/route.js)を生成します。
https://github.com/vercel/workflow/blob/main/packages/builders/src/base-builder.ts
生成されたバンドルは制限付き Node.js VM(Vercel インフラ)で実行される仕組みとなります。
ディレクティブはどうなっていくのだろうか?
Vercel におけるディレクティブの提案は賛否両論分かれているようです。
私が調べてみた範囲では、
- https://useworkflow.dev/docs/how-it-works/understanding-directives には他のプログラミング手法(ジェネレータ/ファイルベース/アノテーション/マクロ…etc)も検証したが、結果的にディレクティブに落ち着いた
-
https://github.com/reactjs/rfcs/pull/189 (RSC 導入時のディスカッション)では当初は
.server.jsと.client.js(あるいは.shared.js)にしようという意見があった
ということなどわかります。
「Run Javascript Everywhere」 が体現される今日、ランタイムを意識してプログラムを書く(どのモジュールをどのバンドルに書くか)ための1つの方法として Vercel ではディレクティブという記法で実装されています。
開発者側にとっては「何をしているかわかりづらい」という意見もありディレクティブがコミュニティに受け入れられるかどうかは不透明ですが、Javascript を普段使う開発者として仕様とその原理的な意味を知っておくことはライブラリ・フレームワークの提供側の気持ちを知るうえでも重要だなと改めて思いました。
▼新卒エンジニア研修のご紹介
レアゾン・ホールディングスでは、2025年新卒エンジニア研修にて「個のスキル」と「チーム開発力」の両立を重視した育成に取り組んでいます。 実際の研修の様子や、若手エンジニアの成長ストーリーは以下の記事で詳しくご紹介していますので、ぜひご覧ください!
▼採用情報
レアゾン・ホールディングスは、「世界一の企業へ」というビジョンを掲げ、「新しい"当たり前"を作り続ける」というミッションを推進しています。 現在、エンジニア採用を積極的に行っておりますので、ご興味をお持ちいただけましたら、ぜひ下記リンクからご応募ください。