前回: Viteのコードを読む - CLI経由でのコア実行の流れ - memo_md
viteパッケージ内部 / サーバー起動部分をよむ
vite dev
vite serve
などで起動する packages/vite/src/node/server
をよむ
ファイル一覧
. ├── __tests__ ├── hmr.ts ├── index.ts ├── middlewares │ ├── base.ts │ ├── error.ts │ ├── indexHtml.ts │ ├── proxy.ts │ ├── spaFallback.ts │ ├── static.ts │ ├── time.ts │ └── transform.ts ├── moduleGraph.ts ├── openBrowser.ts ├── pluginContainer.ts ├── searchRoot.ts ├── send.ts ├── sourcemap.ts ├── transformRequest.ts └── ws.ts
CLI からの起動は ↓
const { createServer } = await import('./server') try { const server = await createServer({ root, base: options.base, mode: options.mode, configFile: options.config, logLevel: options.logLevel, clearScreen: options.clearScreen, server: cleanOptions(options) }) ...
vite/packages/vite/src/node/server/index.ts
# createServer
が該当
createServer
コンフィグの読み込みとサーバー生成
const config = await resolveConfig(inlineConfig, 'serve', 'development')
- inlineConfig は CLI の実行時引数などから生成したオブジェクト
resolveConfig
でResolvedConfig
型の値を返す- inlineConfig の
configFile
がfalse
でなければloadConfigFromFile
で読む (明示的に "使いません!!" としない限りロードする) - 次の順番でロードを試みる
configFile
でファイル指定がある場合はそれをロードvite.config.js
vite.config.mjs
vite.config.ts
vite.config.cjs
- ESM の場合 (package.json の type が
module
、または設定ファイルが.mjs
・.ts
)はbundleConfigFile
- esbuildでビルド
- Pluginとして
externalize-deps
とreplace-import-meta
を適用している - そういうプラグインパッケージがあるわけじゃなく、ただ名前つけてるだけぽい
externalize-deps
→ 外部パッケージをマークreplace-import-meta
→import.meta.url
,__dirname
,__filename
を実体に置き換え
- inlineConfig の
const httpsOptions = await resolveHttpsConfig( config.server.https, config.cacheDir )
vite/packages/vite/src/node/http.ts
#resolveHttpsConfig
- https接続用の ca,cert,key,pfx ファイルをロード
const middlewares = connect() as Connect.Server const httpServer = middlewareMode ? null : await resolveHttpServer(serverConfig, middlewares, httpsOptions) const ws = createWebSocketServer(httpServer, config, httpsOptions)
- connect でサーバのMiddlewareを作成
- server設定で
middlewareMode
が設定されていなければresolveHttpServer
- https://ja.vitejs.dev/config/#server-middlewaremode
index.html
の配布を誰が責務を持つかの話- ドキュメント上では
ssr
orhtml
が設定に渡せるようだが、実際はtrue
false
もいける
- https/proxyの設定に応じて、
http
orhttps
orhttp2
モジュールでcreateServer
(connectで作られたMiddlewareを渡す)
- https://ja.vitejs.dev/config/#server-middlewaremode
createWebSocketServer
: HMR用のWebSocketサーバーの準備。基本的に↑で使っているサーバーをそのまま使う- Midlewareモードだったり
server.htr.server
の設定などによってはサーバーを作る - 先に作ったサーバーを利用する場合は、
upgrade
要求で WebSocket コネクションを確立する
- Midlewareモードだったり
createPluginContainer
const container = await createPluginContainer(config, moduleGraph, watcher)
- そもそも
PluginContainer
とは何なのかを知らないと読めなさそう - https://ja.vitejs.dev/guide/api-javascript.html#vitedevserver
指定したファイル上でプラグインフックを実行できる Rollup プラグインコンテナ。
とある
- https://ja.vitejs.dev/guide/api-plugin.html#%E5%85%B1%E9%80%9A%E3%81%AE%E3%83%95%E3%83%83%E3%82%AF
開発中、Vite 開発サーバは、Rollup が行うのと同じ方法で Rollup ビルドフックを呼び出すプラグインコンテナを作成します。
- 適宜 Rollup ビルドを行うためのコンテナ
// we should create a new context for each async hook pipeline so that the // active plugin in that pipeline can be tracked in a concurrency-safe manner. // using a class to make creating new contexts more efficient class Context implements PluginContext { ... }
次のようなものを持つ(特徴的ぽい一部だけ抜粋)
parse
: acornでコードをparseresolve
: ID(ファイルのURLやパス)を解決addWatchFile
: chokiderの監視対象にファイルを乗せる
都度生成しているケースもあるし、TransformContext
のように継承しているケースもある。
最終的に返される PluginContainer
は次のメソッドを持つ
buildStart
: すべてのプラグインを対象にbuildStart
を呼ぶresolveId
: ID(ファイルのURLやパス)を解決load
: すべてのプラグインを対象に id を引数にload
を呼ぶtransform
: すべてのプラグインを対象に id を引数にtransform
を呼ぶclose
: すべてのプラグインを対象にbuildEnd
とcloseBundle
を呼ぶ
サーバー停止用ハンドラの生成
const closeHttpServer = createServerCloseFn(httpServer)
connection
のたびに全てをopenSockets
として保持- 全ての接続に対して
destroy
で破棄
ViteDevServer
の生成
ここまで作ってきた全てを一通り保持する ViteDevServer
オブジェクトを作る
詳細は次回以降。
感想
createServer
だけでもやってることめっちゃ多かった- vite.config 書くときに「tsでも書けて便利〜〜」とか思ってたけど、裏で普通にesbuildしててそりゃそうだよなってなった
- RollUp用の
PluginContainer
で複雑なことをやってんだなってのはわかった- 実際プラグイン書いてみないと真髄はわからなさそう
次回
createServer
読み終わってないので、続きをよむ